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

System.Text.Json反序列化TCP Socket传输JSON时因末尾多余大括号引发异常的问题求助

解决TCP Socket发送JSON时的粘包导致的JSON反序列化异常

这个问题太常见了——你碰到的是TCP流式传输带来的粘包问题!TCP不会帮你划分消息的边界,连续发送的多个JSON很可能被合并成一段数据接收,或者单个JSON被拆成多段,这就导致你收到的字符串里出现多余的语法符号(比如你例子里多出来的那个},其实是两个JSON粘在一起了:{"Value":3,"Name":"Blood Sugar"}{"Value":4,"Name":"XXX"}),反序列化自然就炸了。

先帮你梳理下当前代码里的几个坑:

  • 你完全忽略了Receive方法的返回值,直接把整个byteMessage数组转成字符串,这会把之前接收的残留数据也包含进去,很容易导致脏数据。
  • 接收端用stringMessage.IndexOf('\0')来截取消息,但发送端根本没发送\0,这个逻辑完全不成立,一旦IndexOf返回-1,Substring直接报错。
  • 你默认每次Receive就能拿到完整的一个JSON,但TCP根本不保证这点,它只按顺序传字节,不管你的消息边界。

下面给你两种靠谱的修复方案,先从最简单的开始:


方案1:用换行符作为消息边界

这个方法简单直观,给每个JSON后面加个换行符\n,接收端累计收到的数据,直到碰到换行符,再处理完整的JSON。

修改后的发送端代码

while (Form.isGenerate) {
    Data newData = dataType.generate(person.generatingParameters);
    Thread.Sleep(500);
    // 序列化后拼接换行符,作为消息结束的标记
    string jsonContent = JsonSerializer.Serialize<Data>(newData) + "\n";
    byte[] sendBytes = Encoding.UTF8.GetBytes(jsonContent);
    clientSocket.Send(sendBytes);
    
    // 接收响应时也要处理实际收到的字节数,别直接用整个数组
    byte[] responseBuffer = new byte[1024];
    int receivedBytes = clientSocket.Receive(responseBuffer);
    string response = Encoding.UTF8.GetString(responseBuffer, 0, receivedBytes);
}

修改后的接收端代码

// 用StringBuilder缓存未处理的残留数据,应对拆包情况
StringBuilder messageBuffer = new StringBuilder();
byte[] receiveBuffer = new byte[1024];

while(true) {
    int actualReceivedBytes = seperateSocketForEachRequest.Receive(receiveBuffer);
    if (actualReceivedBytes == 0) {
        // 客户端断开连接,退出循环
        break;
    }
    
    // 把收到的有效字节转成字符串,加入缓存
    string receivedPart = Encoding.UTF8.GetString(receiveBuffer, 0, actualReceivedBytes);
    messageBuffer.Append(receivedPart);
    
    // 循环处理缓存里所有完整的消息(直到没有换行符为止)
    int newlinePos;
    while ((newlinePos = messageBuffer.ToString().IndexOf('\n')) != -1) {
        // 提取完整的JSON内容(去掉末尾的换行符)
        string completeJson = messageBuffer.ToString().Substring(0, newlinePos);
        // 从缓存中移除已经处理的部分
        messageBuffer.Remove(0, newlinePos + 1);
        
        try {
            // 反序列化,这里建议用具体的Data类型而不是Object,避免后续类型转换麻烦
            Data receivedMessage = JsonSerializer.Deserialize<Data>(completeJson);
            // 在这里处理你的消息...
        } catch (JsonException ex) {
            // 捕获异常,避免程序崩溃,同时可以记录错误日志
            Console.WriteLine($"反序列化失败:{ex.Message} | 错误内容:{completeJson}");
        }
    }
    
    // 发送响应
    seperateSocketForEachRequest.Send(Encoding.UTF8.GetBytes("FF"));
}

方案2:用长度前缀标记消息(更严谨)

如果你的场景对消息可靠性要求更高,比如JSON里可能包含换行符,那可以用「先发送消息长度,再发送消息内容」的方式:

  1. 发送端先把JSON的字节长度转成固定长度的字节(比如4字节的int)发送。
  2. 接收端先接收4字节,解析出长度,再接收对应长度的字节,这样就能准确拿到完整的JSON。

举个简单的发送端示例:

string json = JsonSerializer.Serialize<Data>(newData);
byte[] jsonBytes = Encoding.UTF8.GetBytes(json);
// 把字节长度转成4字节的大端字节数组
byte[] lengthBytes = BitConverter.GetBytes(jsonBytes.Length);
if (BitConverter.IsLittleEndian) {
    Array.Reverse(lengthBytes);
}
// 先发送长度,再发送JSON内容
clientSocket.Send(lengthBytes);
clientSocket.Send(jsonBytes);

接收端对应处理:

byte[] lengthBuffer = new byte[4];
// 先接收长度字节
int lengthReceived = seperateSocketForEachRequest.Receive(lengthBuffer);
if (lengthReceived != 4) {
    // 处理异常,比如客户端断开
    break;
}
if (BitConverter.IsLittleEndian) {
    Array.Reverse(lengthBuffer);
}
int jsonLength = BitConverter.ToInt32(lengthBuffer, 0);

// 接收对应长度的JSON内容
byte[] jsonBuffer = new byte[jsonLength];
int totalReceived = 0;
while (totalReceived < jsonLength) {
    int received = seperateSocketForEachRequest.Receive(jsonBuffer, totalReceived, jsonLength - totalReceived, SocketFlags.None);
    if (received == 0) {
        break;
    }
    totalReceived += received;
}
string completeJson = Encoding.UTF8.GetString(jsonBuffer);
// 反序列化...

最后几个关键提醒

  • 永远不要忽略Receive的返回值:它告诉你实际收到了多少字节,必须用这个值来截取数组,否则残留的旧数据会搞脏你的消息。
  • 别用\0当边界:你发送端没发这个字符,接收端这么做完全是碰运气,而且JSON规范里也不允许字符串包含\0,这个逻辑彻底废掉。
  • 用具体类型反序列化:别用Object,直接反序列化成Data类型,既高效又避免后续类型转换的麻烦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 23:22:46