You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# TCP Client使用BeginRead首次响应后返回全0字节问题排查

C# TCP Client BeginRead读取异常问题修复

问题现象

  • 基于C# TCP Client的BeginRead异步方法实现设备持续监听逻辑,设备按键触发时会主动推送数据包
  • 首次发送10字节请求可正常收到3字节响应,后续发送重复请求时只能拿到长度为1024的全0字节数组
  • Wireshark抓包确认设备响应正常,问题出在客户端代码,第二次请求后设备按键推送也无法正常解析

初始代码

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();
}

问题根因

  1. 未处理EndRead返回值:EndRead的返回值是本次异步读取实际收到的字节数,你当前直接使用整个1024字节的缓冲区,只要实际读取字节数小于3,你统计非0字节就会判定为无有效数据,甚至如果读取还没完成就访问缓冲区,拿到的就是初始的全0值
  2. 重复执行Open操作:每次调用StartRecieveContinuous都会执行Open(),已连接状态下重复打开连接会导致连接重置,后续所有读取都会失败
  3. 缓冲区存在并发风险:用类字段LastRecievedData存储接收缓冲区,多次异步读取操作会互相覆盖缓冲区内容,导致数据丢失
  4. 未处理0字节读取:TCP流中EndRead返回0代表远端已经断开连接,此时继续循环调用BeginRead只会持续返回全0缓冲区

修复后代码

/// <summary>
/// 启动持续监听,Open操作仅在初始化连接时调用一次即可,不要放到该方法内重复执行
/// </summary>
internal void StartReceiveContinuous()
{
    // 仅在连接断开时重新打开连接
    if (Client == null || !Client.Connected)
    {
        Open();
    }

    // 缓冲区改为局部变量,随异步状态传递,避免并发覆盖
    byte[] receiveBuffer = new byte[1024];
    NetworkStream stream = Client.GetStream() ?? throw new Exception("TCP客户端未初始化");
    
    // 将流和缓冲区一起作为异步状态传递
    stream.BeginRead(receiveBuffer, 0, receiveBuffer.Length, CallbackDataReceived, (stream, receiveBuffer));
    ListenerRunning = true;
}

private void CallbackDataReceived(IAsyncResult ar)
{
    ListenerRunning = false;
    var (stream, receiveBuffer) = ((NetworkStream, byte[]))ar.AsyncState;
    
    try
    {
        // 读取本次实际收到的字节数
        int bytesRead = stream.EndRead(ar);
        
        // 返回0说明远端连接已断开,终止监听循环
        if (bytesRead == 0)
        {
            // 可在此处添加连接断开的事件通知
            return;
        }
        
        // 仅截取实际收到的字节部分,避免冗余的0值
        byte[] actualData = receiveBuffer.Take(bytesRead).ToArray();
        
        // 调试输出
        System.Diagnostics.Debug.WriteLine(">>> " + string.Join(" ", actualData.Select(x => x.ToString("X2"))));
        
        if (actualData.Length > 0)
        {
            EventDataRecieved?.Invoke(this, new DataRecievedArgs(actualData));
        }
    }
    catch (IOException ex)
    {
        // 捕获网络异常,避免崩溃
        System.Diagnostics.Debug.WriteLine($"接收数据异常:{ex.Message}");
        return;
    }
    
    // 正常接收完成,启动下一次监听
    StartReceiveContinuous();
}

其他注意事项

发送请求的逻辑不要和接收回调耦合,避免阻塞接收流程导致数据丢失。如果需要按请求响应模式交互,建议维护独立的请求队列和超时机制。

内容的提问来源于stack exchange,提问作者Max N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 19:39:00