“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

  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TCP协议是一种面向连接的可靠传输协议,它将数据分成一个一个的数据包进行传输。但是,由于网络传输的不确定性,TCP粘包和分包问题就会出现。 1. TCP粘包问题 TCP粘包问题是指发送方将多个数据包合并成一个数据包发送,接收方无法区分多个数据包的边界,从而无法正确处理数据包。造成TCP粘包问题的原因有多种,比如发送方发送的数据包过大、发送速度过快、网络延迟等。 解决方法: (1) 设置消息边界标识符 在发送的消息中添加一个特殊的标识符,如换行符、空格等,用来标识消息的边界。接收方根据标识符来判断消息的边界,将消息分隔成多个数据包。 (2) 定长消息 可以设置一个固定长度的消息,每次发送的数据都是定长的。这样接收方就可以根据固定长度来将消息分隔成多个数据包。 2. TCP分包问题 TCP分包问题是指发送方将一个数据包分成多个数据包发送,接收方接收后需要将多个数据包组合成一个完整的数据包,才能进行处理。造成TCP分包问题的原因有多种,比如发送方发送的数据包过大、网络拥塞等。 解决方法: (1) 设置消息长度 在消息中添加消息长度信息,接收方接收到数据后,根据长度信息将多个数据包组合成一个完整的数据包。 (2) 固定长度消息 发送方每次发送的数据都是固定长度的,接收方根据固定长度来将多个数据包组合成一个完整的数据包。 总之,TCP粘包和分包问题可以通过合理的协议设计和网络优化来解决。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值