文章目录
- 1、介绍
- 2、为什么会出现粘包/拆包问题
- 2.1、TCP协议
- 2.2、粘包问题
- 2.3、拆包问题
- 3、粘包/拆包场景
- 4、解决方案
- 4.1、固定长度的数据包
- 4.2、特殊字符或标记
- 4.3、消息头
- 5、为什么UDP没有粘包/拆包问题
1、介绍
在TCP中,粘包和拆包问题是十分常见的,如基于TCP协议的RPC框架、Netty等。
粘包(Packet Stickiness)指的是在网络通信中,发送方连续发送的多个小数据包被接收方一次性接收的现象。这可能是因为底层传输层协议(如TCP)会将多个小数据包合并成一个大的数据块进行传输,导致接收方在接收数据时一次性接收了多个数据包,造成粘连。
拆包(Packet Splitting)指的是在网络通信中,发送方发送的一个大数据包被接收方拆分成多个小数据包进行接收的现象。这可能是因为底层传输层协议(如TCP)将一个大数据包拆分成多个小的数据块进行传输,导致接收方在接收数据时分别接收了多个小数据包,造成拆开。
来自于百度词典:粘包读
zhān bāo
而非nián bāo
2、为什么会出现粘包/拆包问题
2.1、TCP协议
那就需要先了解 TCP 的定义。TCP(Transmission Control Protocol)传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。
其中跟粘包/拆包关系最大的就是基于字节流这个特点。字节流可以理解为一个双向的通道里流淌的数据,这个数据其实就是我们常说的二进制数据,简单来说就是一大堆 01
串。这些 01
串之间没有任何边界。
应用层传到 TCP 协议的数据,不是以消息报为单位向目的主机发送,而是以字节流的方式发送到下游,这些数据可能被切割和组装成各种数据包,接收端接收到这些数据包后没有正确还原之前的消息,因此出现粘包/拆包现象。
2.2、粘包问题
由于TCP是面向流的协议,发送方将数据流切割成数据块并通过网络发送,而接收方则将接收到的数据块重新组装成数据流。当发送方连续发送多个小数据块时,这些数据块可能会被合并成一个大的数据块,从而产生粘包现象。造成粘包问题的主要原因有以下几点:
- 数据发送速度快于接收方处理速度:当发送方连续发送多个小数据块时,由于网络传输延迟等原因,接收方的缓冲区可能无法及时处理所有数据块,从而导致这些数据块被合并成一个大的数据块;
- TCP缓冲区合并:TCP协议在发送端和接收端都有缓冲区,TCP层可能在缓冲区中将多个小数据块合并为一个大数据块再进行传输。
2.3、拆包问题
同样,由于TCP是面向流的协议,发送方将数据流切割成数据块并通过网络发送,而接收方则将接收到的数据块重新组装成数据流。当发送方发送一个较大的数据块时,在网络传输过程中,可能会被拆分成多个小的数据块进行传输,从而产生拆包现象。造成拆包问题的主要原因有以下几点:
- 数据发送速度慢于接收方处理速度:当发送方发送一个较大的数据块时,由于网络传输延迟等原因,接收方的缓冲区可能无法一次性接收所有数据块,从而导致数据被拆分成多个小的数据块;
- TCP缓冲区拆分:TCP协议在发送端和接收端都有缓冲区,TCP层可能在缓冲区中将一个大数据块拆分成多个小数据块再进行传输。
3、粘包/拆包场景
因为TCP是面向流,没有边界,而操作系统在发送TCP数据时,会通过缓冲区来进行优化,例如缓冲区为1024个字节大小。
如果一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,这就形成了粘包问题。
如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就形成了拆包问题。
关于粘包和拆包可以参考下图的几种情况:
- 正常的理想情况,两个包恰好满足TCP缓冲区的大小或达到TCP等待时长,分别发送两个包;
- 粘包:两个包较小,间隔时间短,发生粘包,合并成一个包发送;
- 拆包:一个包过大,超过缓存区大小,拆分成两个或多个包发送;
- 拆包和粘包:Packet1过大,进行了拆包处理,而拆出去的一部分又与Packet2进行粘包处理。
4、解决方案
4.1、固定长度的数据包
发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
下图每个包的固定长度为 4,接收端很容易进行区分。
4.2、特殊字符或标记
在数据包中使用特殊的字符或标记来标识数据包的边界,接收端根据特殊字符或标记进行数据解析和处理。
如下图,在每个包的后面加上特殊字符:/
4.3、消息头
在数据包的开头添加一个消息头,用来记录数据包的长度或其他信息,接收端先读取消息头来确定数据包的长度,然后根据长度进行数据解析和处理。
如下图,在每个包前面加上包的实际长度。
当然,还有一种常见的方法就是通过自定义协议进行粘包和拆包的处理。但对于个人开发而已,这种成本就相对比较高了。
5、为什么UDP没有粘包/拆包问题
UDP(User Datagram Protocol)是一个无连接的、不可靠的传输协议,与TCP不同,它并不提供像TCP那样的可靠数据传输和流控制机制。因为UDP没有像TCP那样的字节流特性,所以在UDP中不存在粘包和拆包问题。
在UDP中,每个数据报(Datagram)都是独立的,包含了完整的数据和目标地址信息。每次发送的数据都会被封装成一个独立的数据报,而接收端也会逐个接收每个数据报,不会将多个数据报合并成一个大的数据块,也不会将一个大的数据块拆分成多个小的数据块。
由于UDP的无连接特性,它不会进行流控制、拥塞控制和重传等机制,因此在传输过程中可能会丢失数据包,也可能会乱序。但是,由于每个数据报都是独立的,所以在UDP中不存在粘包和拆包问题。