tcp拆包问题求高手指点下非常感谢。

现在用golang tcp 进行接收数据。
数据格式:{"xx":"xx"} 标准的josn
但是现在有以下情况出现 出现半包情况
第一次收到数据

{"xx":"xx"}{"xx"

第二次读到

:"xx"}{"xx":"xx"}

以上情况如何进行准确的拆包呢?
(现在已经无法更改数据协议 只能按照这种)

阅读 2.1k
3 个回答

基于 TCP 的报文啥时候算结束不得你们自己约定么?

比如 HTTP,也是基于 TCP 的,它会在某一行读到 Content-Length,这个值就是预计的报文总长度,然后读这么多以后就认为所有 TCP 包都收到了,按顺序拼接到一起,就是一个完整的 HTTP 报文。

你们在设计报文协议的时候也得有一个机制,好让对方知道啥时候算发完了。

P.S. 主流的基于 TCP 的协议都会不约而同地有正文的概念,比如 HTTP、SSH、gRPC、dubbo 等等,你可以思考一下为什么。你这里的只有正文,而没了头。

这是典型的 TCP 粘包问题,你们在设计传输协议之前完全没考虑到这方面。粘包产生的原因主要是以下情况,

  1. SQ_SNDBUF 套接字本身有缓冲区 (发送缓冲区、接受缓冲区)
  2. tcp 传送的端 mss 大小限制
  3. 链路层也有 MTU 大小限制 ,如果数据包大于 >MTU 要在 IP 层进行分片,导致消息分割。
  4. tcp 的流量控制和拥塞控制,也可能导致粘包
  5. tcp 延迟发送机制等

处理粘包的方式也有很多,比如,

  1. 定长包。也就是发出去的数据每次都是定长的,这由双方早已商定好了,各自读取定长就好。但我觉得这个方式太鸡肋。
  2. 特殊分隔符,比如 \n\r\n...当遇到些符号时表示一个包的数据发送完毕,但这种方式效率极低。为什么?因为你读出来要去字符串查找啊。
  3. 自定义协议。这是目前大家最常用,也是最标准,最具有扩展性的方法。比如常见的自定义协议是这样的,
功能码(2字节)+ payload-len(4字节)+payload

其中 payload 的长度取决于 payload-len 的无符号整型数字(大小端问题自己CS两端协商搞定)。

那么这样的协议,如何来读取呢?下面是一段示例程序,

// 1. 读取功能码
ret = read(fd, buf1, 2);

// 2. 根据功能码判断是否合法
...

// 3. 读取 payload-len
ret = read(fd, buf2, 4);

// 4. 如果第三步成功
uint32_t n;
n = ((uint32_t)buf2[1] << 24) | ((uint32_t)buf2[2] << 16) | ((uint32_t)buf2[3] << 8) | ((uint32_t)buf2[4]);

// 5. 读取 payload
ret = read(fd, buf3, n);

按照你们现有无可奈何的机制,我建议在 json 末尾加入特殊符号来达到切割 payload 的效果。

建议重新写呢, 没办法解决 除非你们是短连接 发送一次就断开连接, 那样的话 可以知道 结束就是 EOF 否则真的没办法确认边界

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题