粘包指多个应用层消息被合并到一次读取中,拆包指一个消息被拆成多次读取。根本原因是 TCP 是面向字节流的协议,不保留消息边界,加上 Nagle 算法合并小包、MSS 分段和接收缓冲区的影响,应用层读到的是无边界字节流。

解决方案(在应用层协议中约定帧格式):

  1. 固定长度:每条消息定长,不足填充,简单但浪费带宽。
  2. 分隔符:用特殊字符(如 \n)标记消息结束,需转义内容中的分隔符。
  3. 长度字段(最常用):消息头携带 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 无粘包问题,因为它保留报文边界。