c# - 在给定字节的第一次响应后,使用 BeginRead DataReturned 的 TCP 客户端为 NULL
问题描述
我正在努力使用 C# TCP Client BeginRead 方法。
我正在与作为服务器的机器通信。
由于按下按钮时机器会发送推送数据包,因此我需要一直听。我正在通过 BeginRead(..) 异步执行此操作。
对于发送到机器的第一个请求(10 字节),响应被正确读取(3 字节)。如果我重复其他类型的请求,BeginRead 只会返回一个 00 字节(长度 1024)的数组。
Wireshark 截图证明这是我的 c# 应用程序的问题。
在第二个请求之后,来自机器的推送消息也不起作用。
internal void StartRecieveContinuous()
{
Open();
int dataLenght = 1024;
this.LastRecievedData = new byte[dataLenght];
NetworkStream stream = Client?.GetStream() ?? throw new Exception("The TCP Client wasn't initialized!");
stream.BeginRead(LastRecievedData, 0, dataLenght, CallbackDataRecieved, stream);
ListenerRunning = true;
}
private void CallbackDataRecieved(IAsyncResult ar)
{
ListenerRunning = false;
// Debug
System.Diagnostics.Debug.WriteLine(string.Join(" ",this.LastRecievedData.Take(20).Select(x => x.ToString("X"))));
// Debug
if((this.LastRecievedData?.Count(x => x > 0) ?? 0) > 0)
{
EventDataRecieved?.Invoke(this, new DataRecievedArgs(this.LastRecievedData.Take(this.LastRecievedData?.Length ?? 0).ToArray()));
}
StartRecieveContinuous();
}
调试控制台日志(十六进制):
5 1 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (第一反应)
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (第二次响应)
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0(按钮按下事件数据)
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0(按钮释放事件数据)
编辑:
我尝试使用 endRead 但我的问题仍然存在:
private void CallbackDataRecieved(IAsyncResult ar)
{
NetworkStream myNetworkStream = (NetworkStream)ar.AsyncState;
myNetworkStream.EndRead(ar);
ListenerRunning = false;
System.Diagnostics.Debug.WriteLine(">>> " + string.Join(" ", this.LastRecievedData.Take(20).Select(x => x.ToString("X"))));
if((this.LastRecievedData?.Count(x => x > 0) ?? 0) > 0)
{
EventDataRecieved?.Invoke(this, new DataRecievedArgs(this.LastRecievedData.Take(this.LastRecievedData?.Length ?? 0).ToArray()));
}
StartRecieveContinuous();
}
解决方案
基本上,您需要调用EndRead
回调,直到您这样做:您处于未定义的行为领域,试图执行多个重叠的读取操作。澄清:EndRead
不会导致读取操作结束;相反:BeginFoo
并且EndFoo
只是给定模式的异步操作的两个部分,它们反映了同步Foo
调用。
您是正确的(评论),它EndRead
仅返回读取的字节数,但与非异步Read
API 返回的内容完全一致;同样,数据存储在您传入的缓冲区中。就像使用 一样Read
,您还需要注意返回的整数EndRead
;这是您从阅读中获得的最重要的东西!您不应尝试从您提供的缓冲区中处理超过这么多的数据。
然而!老实说,在显示的代码中,有很多东西需要区分——线程、缓冲区管理、同步与异步操作;手动使用时,TCP API 很难编写。如果您使用的是 .NET Core / .NET 5 或更高版本,那么老实说:您可以使用“管道”API 将所有这些移动到 Kestrel,该 API 会为您处理所有这些杂乱无章的代码,因此您只需要write 是您的协议代码(实际的数据解析/处理代码)。可以在这个多部分系列中找到对此的概述