You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

从NetworkStream读取数据出现损坏,多消息场景下问题复现

C++ TCP服务器消息读取损坏问题排查与修复

我看了你这段C#读取TCP消息的代码,遇到高并发消息(超过10条)时出现损坏,核心问题出在消息头解析错误读取逻辑的几个漏洞上,咱们一步步拆解:

首先说最致命的问题:消息长度解析完全错了

你的C++服务器是用前3字节二进制表示消息长度,但你现在把这3个字节直接转成ASCII字符串再Int32.TryParse,这完全不对。举个例子:如果实际消息长度是120,二进制是0x00 0x00 0x78,转成ASCII后是两个空字符加一个'x',TryParse根本得不到正确的120,最后number会是0,后续读取逻辑直接乱套,这就是大量消息过来时数据损坏的核心原因。

其他几个坑点

  • Thread.Sleep(10)是非常不靠谱的“凑数”逻辑,网络延迟是不确定的,要么等数据可用,要么用异步读取,靠sleep很容易导致读少或者读多。
  • 读取完消息体后,你直接把_temp数组整个转成字符串赋值给message,但_temp的长度是number+1,如果实际没读满,后面的字节都是默认的0,转成ASCII就是乱码或者问号。
  • 剩余数据读取的长度计算错误,循环里应该用number - totalByetsRead,而不是number+1 - numBytesRead,而且没有处理Read返回0的情况(比如连接断开)。

修复后的代码

我重新写了读取逻辑,解决了上面所有问题:

private void RecieveMsg(out string message)
{
    message = string.Empty;
    byte[] lengthBytes = new byte[3];
    int bytesRead = 0;

    // 先确保读满3字节的长度头
    while (bytesRead < 3)
    {
        int read = _netStream.Read(lengthBytes, bytesRead, 3 - bytesRead);
        if (read == 0)
        {
            // 连接已断开,直接返回空
            return;
        }
        bytesRead += read;
    }

    // 解析3字节的长度(注意字节序,假设C++服务器是大端序,根据实际情况调整)
    int messageLength = (lengthBytes[0] << 16) | (lengthBytes[1] << 8) | lengthBytes[2];
    if (messageLength <= 0)
    {
        // 无效长度,直接返回
        return;
    }

    byte[] messageBytes = new byte[messageLength];
    bytesRead = 0;

    // 读取完整的消息体
    while (bytesRead < messageLength)
    {
        int read = _netStream.Read(messageBytes, bytesRead, messageLength - bytesRead);
        if (read == 0)
        {
            // 连接断开,读取不完整,返回已读部分或者空
            message = Encoding.ASCII.GetString(messageBytes, 0, bytesRead);
            return;
        }
        bytesRead += read;
    }

    // 转成最终的字符串
    message = Encoding.ASCII.GetString(messageBytes);
}

关键修复点说明

  1. 正确解析二进制长度头:把3个字节按二进制位拼接成整数,这里假设C服务器用的是大端序(网络字节序),如果你的C代码是小端序,要改成(lengthBytes[2] << 16) | (lengthBytes[1] << 8) | lengthBytes[0],这个一定要对应上。
  2. 循环读取确保读满:不管是长度头还是消息体,都用循环读取直到读够指定字节数,避免因为TCP粘包/拆包导致的读取不完整。
  3. 处理连接断开情况:当Read返回0时,说明对方已经断开连接,直接终止读取,避免死循环。
  4. 去掉不靠谱的Sleep:完全依赖Read的返回值和循环来控制读取,不需要靠sleep等待数据。

这样修改后,不管多少条消息过来,都能正确解析每个消息的长度,然后完整读取消息体,不会再出现乱码或者损坏的情况。

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

火山引擎 最新活动