“TCP粘包”不是TCP的问题!

前言

写RPC用了Netty。涉及到粘包拆包问题。想复习一下。发现网上博客多是概念模糊不清。没有触及本质或者没有讲清楚。

遂决定自己写一篇

"TCP粘包"是谁的问题?

首先我们要明确TCP是面向字节流的协议。也就是说我们在应用层想使用TCP来传输数据时,它是无法区分消息的。具体举个例子。

我们想发两条消息。一个100字节,一个1000字节。

我们调用两次TCP的send()。send函数意味着把数据拷贝到发送缓冲区,若缓冲区不够全部写入则会分次写入。

这里我们假设发送缓冲区大小是不变的,仅是TCP的滑动窗口在变。

设我们的缓冲区有1400的大小,此时发送窗口为1100字节。

则写入缓冲区是这个状态,前1100是发送窗口,存放我们要发的两条消息。

但是实际上在缓冲区中都是字节数据,TCP是不会区分消息的,只会把这1100字节视为字节流来进行传输,包装为一个TCP报文来发送。也就是说TCP眼中看这1100个字节就是单纯的字节流,没有我们眼中的消息1,消息2之分。

然后由于Nagle算法,在一段时间后没有等到包,即使没到MSS也会发出。这样这两个消息作为一个TCP包中的数据被发了数据

这就发生了粘包。TCP并不区分应用层的消息边界,只会按发送窗口来发送字节数据。这导致在应用层中本来是两条的消息封装到一个TCP报文,服务端接收会该报文会读出两个粘连的消息。所以需要我们进行协议设计来解决这个粘包的问题。

注意:发送缓冲区等待MSS大小才发送是Nagle协议做的事。下文有介绍。

那么为什么说粘包不是TCP的问题?

因为TCP本身就是针对字节流传输的数据。按消息分割是我们使用者的需求,自然应当有我们自己去解决。

所以准确来说TCP粘包其实是使用TCP传输有边界的消息导致的消息粘连问题。

粘包或半包的原因

滑动窗口让我们可以发送多个数据包而无需等待确认。也即累计确认。

在思考过程中我开始纠结,连续发送多个包的大小是多少,是否都会是标准的MSS。但是实际上这个问题是与粘包半包无关的。TCP把多少字节数据封装为一个TCP报文并没有关系。

问题本质在于TCP字节流传输的性质。

只要基于字节流,那TCP必然无法区分我们应用层划分的多个数据包。无论TCP把数据怎么划分为TCP报文,都有出现粘包半包的可能性。

Nagle算法要MSS再发送是导致粘包半包发生的可能原因,那么我们关掉它,是否可以让缓冲区一有数据就发送呢?是否就能分消息发送了?

Nagle算法

Nagle算法是为了保证TCP报文尽量达到MSS大小。反正基于字节流不必按消息封装报文。只需要按字节流顺序封装即可。为什么要尽量达到MSS呢?

比如我们想发送1000条2字节的消息。若是每次立即发出就是1000个包,而TCP首部有40字节,body却只有2字节。这严重浪费了网络带宽。我们完全可以等待直到可以封装出一个MSS大小的包。

这样一次性发送了MSS长度的数据,只用了一个首部。大大提高了效率。

当然若迟迟等不到MSS大小的数据,它也会直接发送当前大小的数据包。

目前由于Nagle会提高延迟已经很少使用。

那么回到上段末尾的问题,关了Nagle算法是否就没了粘包半包?

当然不是。关了它不代表,操作系统会立即发送缓存区的数据。

假设这一种情况,我调用一次send,数据拷贝到内核缓冲区。由于没有Nagle算法,TCP直接发送。

此时我有send两条消息a和b,那么此时TCP正在发送上一个TCP报文,消息a到达缓冲区时TCP无法封装并发送,紧接着消息b也到达缓冲区。此时两条消息都在缓冲区。TCP再发送还是可能发送粘包半包问题(两条小于MSS则是粘包,大于MSS,则分两条会导致半包)。

同理在接受方的缓冲区仍然可能发生类似问题。因为接受缓冲区中存的是从TCP报文中提出来的字节数据。在我们应用层没有read时,它可能累计多条消息的字节数据。仍然可能发生粘包

结语

所以导致粘包半包的原因其实最底层还是TCP的字节流传输性质。

无论是Nagle算法还是不使用Nagle算法亦或者说MSS的限制,究其本质都是因为字节流协议,本身不区分消息边界,视角是字节。

因此,我们在写传输消息格式的需求,若使用TCP协议,一定要考虑这个问题,制定协议来解决。

回到标题,使用TCP协议发送有消息边界的数据,一定要自己解决。因为TCP很明确就是一个传输字节流的协议,不能按照消息来发送数据。

参考文章:

https://segmentfault.com/a/1190000039691657

相关推荐
Yang96115 小时前
光缆故障快速排查实操技巧,鼎讯信通 Smart-S3 OTDR 一线使用心得
网络
2401_894915536 小时前
GEO 搜索优化完整源码从零部署:环境配置、集群搭建全流程
开发语言·python·tcp/ip·算法·unity
LDZKKJ6 小时前
OpenAI模型“越狱“入侵Hugging Face——AI安全史上的至暗时刻
网络·人工智能·安全
xqqxqxxq8 小时前
Java Socket 多人聊天室(私聊+群聊)技术笔记(V4版本)
java·网络·笔记
泡沫冰@9 小时前
上章节中文件的讲解
前端·网络·nginx
SilentSlot11 小时前
【C/C++】手写 DPDK 协议栈(十一):基于 PPS、SYN 比例和源 IP 熵的 DDoS 检测
c语言·c++·tcp/ip
海域云-罗鹏11 小时前
分支访问总部ERP卡顿?如何部署SD-WAN组网解决方案
服务器·网络·安全
Shell运维手记11 小时前
ARP 协议超详细讲解(适合网工考试 / 运维理解)
运维·网络·网络协议·智能路由器
优化Henry12 小时前
学习笔记之VoNR语音业务注册流程
网络·笔记·学习·5g·信息与通信
ShineWinsu13 小时前
对于Linux:HTTP中cookie、session的解析
linux·网络·c++·网络协议·http·cookie·session