与粘包关系最大的就是基于字节流这个特点。
字节流可以理解为一个双向的通道里流淌的数据,这个数据其实就是我们常说的二进制数据,简单来说就是一大堆 01 串。这些 01 串之间没有任何边界。
应用层传到 TCP 协议的数据,不是以消息报为单位向目的主机发送,而是以字节流的方式发送到下游,这些数据可能被切割和组装成各种数据包,接收端收到这些数据包后没有正确还原原来的消息,因此出现粘包现象。
为什么要组装发送的数据?
我们知道TCP 切割数据包是为了能顺利通过网络这根水管。相反,还有一个组装的情况。如果前后两次 TCP 发的数据都远小于 MSS,比如就几个字节,每次都单独发送这几个字节,就比较浪费网络 io 。
所以就有了 Nagle 算法优化,目的是为了避免发送小的数据包。
在 Nagle 算法开启的状态下,数据包在以下两个情况会被发送:
-
如果包长度达到
MSS
(或含有Fin
包),立刻发送,否则等待下一个包到来;如果下一包到来后两个包的总长度超过MSS
的话,就会进行拆分发送; -
等待超时(一般为
200ms
),第一个包没到MSS
长度,但是又迟迟等不到第二个包的到来,则立即发送。
此时发送的这些数据包都是一整个 01 串,如果处理开发者把第一次收到的 msg1 + msg2 就当做是一个完整消息进行处理,就会看上去就像是两个包粘在一起,就会导致粘包问题。
关掉Nagle算法就不会粘包了吗?
Nagle 算法其实是个有些年代的东西了,诞生于 1984 年。对于应用程序一次发送一字节数据的场景,如果没有 Nagle 的优化,这样的包立马就发出去了,会导致网络由于太多的包而过载。
但是今天网络环境比以前好太多,Nagle 的优化帮助就没那么大了。而且它的延迟发送,有时候还可能导致调用延时变大,比如打游戏的时候,你操作如此丝滑,但却因为 Nagle 算法延迟发送导致慢了一拍,就问你难受不难受。
所以现在一般也会把它关掉。
看起来,Nagle 算法的优化作用貌似不大,还会导致粘包"问题"。那么是不是关掉这个算法就可以解决掉这个粘包"问题"呢?
TCP_NODELAY = 1
关闭Nagle就不会粘包了吗
-
接受端应用层在收到 msg1 时立马就取走了,那此时 msg1 没粘包问题
-
**msg2 **到了后,应用层在忙,没来得及取走,就呆在 TCP Recv Buffer 中了
-
**msg3 **此时也到了,跟 msg2 和 msg3 一起放在了 TCP Recv Buffer 中
-
这时候应用层忙完了,来取数据,图里是两个颜色作区分,但实际场景中都是 01 串,此时一起取走,发现还是粘包。
因此,就算关闭 Nagle 算法,接收数据端的应用层没有及时读取 TCP Recv Buffer 中的数据,还是会发生粘包。