网站制作app免费软件,便宜的网站制作,免费网站模板下载,网络营销和市场营销的区别前言
写RPC用了Netty。涉及到粘包拆包问题。想复习一下。发现网上博客多是概念模糊不清。没有触及本质或者没有讲清楚。 遂决定自己写一篇
“TCP粘包”是谁的问题#xff1f;
首先我们要明确TCP是面向字节流的协议。也就是说我们在应用层想使用TCP来传输数据时#xff0c;…前言
写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