首页 > 解决方案 > 在给定字节的第一次响应后,使用 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();
}

标签: c#tcptcpclienttcplistener

解决方案


基本上,您需要调用EndRead回调,直到您这样做:您处于未定义的行为领域,试图执行多个重叠的读取操作。澄清:EndRead不会导致读取操作结束;相反:BeginFoo并且EndFoo只是给定模式的异步操作的两个部分,它们反映了同步Foo调用。

您是正确的(评论),它EndRead仅返回读取的字节数,但与非异步ReadAPI 返回的内容完全一致;同样,数据存储在您传入的缓冲区中。就像使用 一样Read,您还需要注意返回的整数EndRead;这是您从阅读中获得的最重要的东西!您不应尝试从您提供的缓冲区中处理超过这么多的数据。


然而!老实说,在显示的代码中,有很多东西需要区分——线程、缓冲区管理、同步与异步操作;手动使用时,TCP API 很难编写。如果您使用的是 .NET Core / .NET 5 或更高版本,那么老实说:您可以使用“管道”API 将所有这些移动到 Kestrel,该 API 会为您处理所有这些杂乱无章的代码,因此只需要write 是您的协议代码(实际的数据解析/处理代码)。可以在这个多部分系列中找到对此的概述


推荐阅读