粘包指多个应用层消息被合并到一次读取中,拆包指一个消息被拆成多次读取。根本原因是 TCP 是面向字节流的协议,不保留消息边界,加上 Nagle 算法合并小包、MSS 分段和接收缓冲区的影响,应用层读到的是无边界字节流。
解决方案(在应用层协议中约定帧格式):
- 固定长度:每条消息定长,不足填充,简单但浪费带宽。
- 分隔符:用特殊字符(如 \n)标记消息结束,需转义内容中的分隔符。
- 长度字段(最常用):消息头携带 body 长度,先读头部再按长度读 body。HTTP 的 Content-Length、Redis 协议都用这种思路。
示例(Node.js 解码长度前缀帧):
// 帧格式: [4字节长度][body]
function decode(buffer) {
const messages = [];
while (buffer.length >= 4) {
const len = buffer.readUInt32BE(0);
if (buffer.length < 4 + len) break; // 半包,等更多数据
messages.push(buffer.slice(4, 4 + len));
buffer = buffer.slice(4 + len);
}
return { messages, rest: buffer };
}
易错点:UDP 无粘包问题,因为它保留报文边界。