TCP 没有「消息」这个概念
新手写 socket 程序几乎都会撞上同一个困惑:发送端调了三次 send,接收端一次 recv 收到了全部;或者反过来,一次发送分两次才收齐。这就是俗称的"粘包/拆包"。
先纠正一个说法:这不是 TCP 的缺陷。TCP 是字节流协议,它只保证字节的顺序和可靠性,从设计上就不存在"消息边界"。你发的每条"消息",在 TCP 眼里只是字节序列的片段。发送端 Nagle 算法会攒小包,接收端缓冲区会按到达顺序吐字节——边界本来就不该存在。

所以解决方案只有一种思路:在应用层自己画边界。三种经典画法:
- 固定长度:每帧 32 字节,凑够就解析。最简单,只适合结构固定的协议
- 分隔符:帧尾放特殊标记(比如
)。文本协议常用,正文里出现分隔符就要转义,麻烦 - 长度前缀:帧头写明正文长度,先读头再读体。二进制协议的标准答案,HTTP 的 Content-Length 就是这个思路
工程上还有两个放大器要注意:Nagle 算法会把小包合并(低延迟场景 TCP_NODELAY 关掉它),接收端缓冲区不够时一次只能吐出半条消息——所以读取循环必须能处理"数据还没到齐"的状态,按协议状态机读,而不是按 read 次数读。
把"字节流"这三个字真正吃透,粘包问题就从玄学变成了几何题。