TCP 协议粘包问题

当应用层协议使用 TCP 协议传输数据时,TCP 协议可能会将应用层发送的数据分成多个包依次发送,而数据的接收方收到的数据段可能有多个『应用层数据包』组成,所以当应用层从 TCP 缓冲区中读取数据时发现粘连的数据包时,需要对收到的数据进行拆分。

粘包并不是 TCP 协议造成的,它的出现是因为应用层协议设计者对 TCP 协议的错误理解,忽略了 TCP 协议的定义并且缺乏设计应用层协议的经验。本文将从 TCP 协议以及应用层协议出发,分析我们经常提到的 TCP 协议中的粘包是如何发生的:

  • TCP 协议是面向字节流的协议,它可能会组合或者拆分应用层协议的数据;
  • 应用层协议的没有定义消息的边界导致数据的接收方无法拼接数据;

TCP 协议是面向连接的、可靠的、基于字节流的传输层通信协议,应用层交给 TCP 协议的数据并不会以消息为单位向目的主机传输,这些数据在某些情况下会被组合成一个数据段发送给目标的主机。

Nagle 算法是一种通过减少数据包的方式提高 TCP 传输性能的算法。因为网络 带宽有限,它不会将小的数据块直接发送到目的主机,而是会在本地缓冲区中等待更多待发送的数据,这种批量发送数据的策略虽然会影响实时性和网络延迟,但是能够降低网络拥堵的可能性并减少额外开销。

Nagle 算法确实能够在数据包较小时提高网络带宽的利用率并减少 TCP 和 IP 协议头带来的额外开销,但是使用该算法也可能会导致应用层协议多次写入的数据被合并或者拆分发送,当接收方从 TCP 协议栈中读取数据时会发现不相关的数据出现在了同一个数据段中,应用层协议可能没有办法对它们进行拆分和重组。

消息边界

在应用层协议中,最常见的两种解决方案就是基于长度或者基于终结符(Delimiter)。

除了这两种方式之外,我们可以基于特定的规则实现消息的边界,例如:使用 TCP 协议发送 JSON 数据,接收方可以根据接收到的数据是否能够被解析成合法的 JSON 判断消息是否终结。

总结

TCP 协议粘包问题是因为应用层协议开发者的错误设计导致的,他们忽略了 TCP 协议数据传输的核心机制 — 基于字节流,其本身不包含消息、数据包等概念,所有数据的传输都是流式的,需要应用层协议自己设计消息的边界,即消息帧(Message Framing),我们重新回顾一下粘包问题出现的核心原因:

  1. TCP 协议是基于字节流的传输层协议,其中不存在消息和数据包的概念;
  2. 应用层协议没有使用基于长度或者基于终结符的消息边界,导致多个消息的粘连;

发生TCP粘包或拆包有很多原因,现列出常见的几点, 1、要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。 2、待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。 3、要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包。 4、接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

Reference

1 为什么 TCP 协议有粘包问题

2 Socket 粘包和分包问题

3 TCP粘包,拆包及解决方法、丢包的原因及解决办法

results matching ""

    No results matching ""