Back to Blog
By AriesZhou · · 3 min read

TCP Packet Coalescing: Root Cause and Solutions

Network Protocols

Many people treat “packet coalescing” as a TCP bug, but TCP really doesn’t deserve the blame. Understand packet coalescing and how to correctly handle message boundaries at the application layer.

First, packet coalescing is not a TCP problem. The various articles about the “TCP packet coalescing problem” listed in search engines are actually about coalescing caused by poorly designed application-layer protocols that use TCP for data transmission. It is not TCP’s fault.

TCP is a stream protocol and has no concept of packet coalescing.

The so-called packet coalescing problem refers to a situation where multiple messages from the sender are concatenated together when they arrive at the receiver, making them impossible to parse. To solve this problem, you need to design the application-layer protocol properly and agree on message boundaries, so that even if multiple messages are concatenated, the receiver can still locate the boundaries according to the protocol and parse them correctly.

The reason many people regard packet coalescing as caused by TCP is that the “concatenation” behavior occurs at the TCP layer. Data sent and received through TCP is in stream format. When an application-layer protocol uses TCP to transmit data, TCP may split messages sent by the application layer into multiple packets sent sequentially, or combine multiple messages before sending them. As a result, a data segment received by the receiver may consist of multiple messages, which is packet coalescing.

In reality, TCP is a protocol based on byte streams rather than message packets. It guarantees in-order delivery of the byte stream, and parsing the byte stream should be done by the application-layer protocol. Therefore, the packet coalescing problem is actually about “how to design an application-layer protocol”.

graph LR
    subgraph 发送端
        A["消息 A"] -->|TCP| B[字节流]
        C["消息 B"] -->|TCP| B
        D["消息 C"] -->|TCP| B
    end

    subgraph TCP 层
        B -->|可能合并/拆分| E[传输单元]
    end

    subgraph 接收端
        E -->|字节流| F[应用层缓冲区]
        F -->|无边界标识| G["粘在一起:A+B+C"]
    end

    style G fill:#ffcccc

Solutions

The application layer can define message boundaries in the following ways:

ApproachPrincipleSuitable scenarios
Fixed lengthEvery message has a fixed lengthSimple, but wastes bandwidth
Length prefixSend the length first, then the dataGeneral-purpose solution
DelimiterSeparate messages with special charactersText protocols (such as HTTP)
HybridLength + delimiterComplex protocols
// 长度前缀方案示例
// 发送端
const send = (socket, message) => {
  const data = JSON.stringify(message);
  const length = Buffer.byteLength(data);
  const lengthBuffer = Buffer.alloc(4);
  lengthBuffer.writeUInt32BE(length);
  socket.write(lengthBuffer);
  socket.write(data);
};

// 接收端
const buffer = Buffer.alloc(0);
socket.on('data', (chunk) => {
  buffer = Buffer.concat([buffer, chunk]);
  while (buffer.length >= 4) {
    const length = buffer.readUInt32BE(0);
    if (buffer.length >= 4 + length) {
      const message = JSON.parse(buffer.slice(4, 4 + length).toString());
      buffer = buffer.slice(4 + length);
      console.log('收到消息:', message);
    } else {
      break;
    }
  }
});