「 计算机网络 」TCP的粘包/拆包问题
参考&鸣谢
大病初愈,一分钟看懂TCP粘包拆包 雷小帅
TCP 的粘包拆包以及解决方案 一乐说
文章目录
- 「 计算机网络 」TCP的粘包/拆包问题
- 一、前言
- 二、为什么UDP没有粘包
- 三、粘包拆包发生场景
- 四、常见的解决方案
- 五、Netty对粘包和拆包问题的处理
- 六、小结
一、前言
当我们在进行网络传输时,由于各种原因,数据包的发送和接收可能会出现粘包和拆包的问题。粘包和拆包都是数据分组错误的情况,其中粘包指的是多个数据包被合并成一个,而拆包则是一个数据包被拆成了多个数据包。这些问题会导致数据的解析和处理变得困难,进而影响整个系统的稳定性和可靠性。
TCP协议是一种面向连接、可靠的传输协议,在网络传输中被广泛应用。TCP采用了一系列机制来解决粘包和拆包问题,保证数据的可靠传输。本文将介绍TCP协议如何解决粘包和拆包问题,让读者更深入地了解TCP协议的传输特点和实现方式。
二、为什么UDP没有粘包
粘包拆包问题在数据链路层、网络层以及传输层都有可能发生。日常的网络应用开发大都在传输层进行,由于UDP有消息保护边界,不会发生粘包拆包问题,因此粘包拆包问题只发生在TCP协议中。
UDP协议没有粘包的概念,因为它是面向无连接的协议。在UDP中,每个数据报都是独立的,发送方发送多少数据报,接收方就会收到多少数据报。UDP不会对数据进行任何拆分或重组,也不会对数据包进行排序,因此不存在数据包粘连的问题。另外,UDP中也没有类似于TCP的流量控制和拥塞控制机制,所以即使出现丢包或延迟,发送方也不会调整发送速率,从而导致粘包的情况。
三、粘包拆包发生场景
因为TCP是面向流,没有边界,而操作系统在发送TCP数据时,会通过缓冲区来进行优化,例如缓冲区为1024个字节大小。
如果一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,这就形成了粘包问题。
如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就是拆包。
关于粘包和拆包可以参考下图的几种情况:
粘包/拆包
上图中演示了以下几种情况:
- 正常的理想情况,两个包恰好满足TCP缓冲区的大小或达到TCP等待时长,分别发送两个包;
- 粘包:两个包较小,间隔时间短,发生粘包,合并成一个包发送;
- 拆包:一个包过大,超过缓存区大小,拆分成两个或多个包发送;
- 拆包和粘包:Packet1过大,进行了拆包处理,而拆出去的一部分又与Packet2进行粘包处理。
四、常见的解决方案
对于粘包和拆包问题,常见的解决方案有四种:
- 发送端将每个包都封装成固定的长度,比如100字节大小。如果不足100字节可通过补0或空等进行填充到指定长度;
- 发送端在每个包的末尾使用固定的分隔符,例如\r\n。如果发生拆包需等待多个包发送过来之后再找到其中的\r\n进行合并;例如,FTP协议;
- 将消息分为头部和消息体,头部中保存整个消息的长度,只有读取到足够长度的消息之后才算是读到了一个完整的消息;
- 通过自定义协议进行粘包和拆包的处理。
五、Netty对粘包和拆包问题的处理
Netty对解决粘包和拆包的方案做了抽象,提供了一些解码器(Decoder)来解决粘包和拆包的问题。如:
- LineBasedFrameDecoder:以行为单位进行数据包的解码;
- DelimiterBasedFrameDecoder:以特殊的符号作为分隔来进行数据包的解码;
- FixedLengthFrameDecoder:以固定长度进行数据包的解码;
- LenghtFieldBasedFrameDecode:适用于消息头包含消息长度的协议(最常用);
基于Netty进行网络读写的程序,可以直接使用这些Decoder来完成数据包的解码。对于高并发、大流量的系统来说,每个数据包都不应该传输多余的数据(所以补齐的方式不可取),LenghtFieldBasedFrameDecode更适合这样的场景。
六、小结
TCP协议粘包拆包问题是因为TCP协议数据传输是基于字节流的,它不包含消息、数据包等概念,需要应用层协议自己设计消息的边界,即消息帧(Message Framing)。如果应用层协议没有使用基于长度或者基于终结符息边界等方式进行处理,则会导致多个消息的粘包和拆包。
针对 TCP 粘包拆包的现象,常见的解决思路如下:
(1)发送端给每个数据包添加包首部。
(2)发送端将每个数据包封装为固定长度
(3)可以在数据包之间设置边界(特殊字符串分割)。
虽然很多框架中都有现成的解决方案,比如Netty,但底层的原理我们还是要清楚的,而且还要知道有这么回事,才能更好的结合场景进行使用。