6tcp粘包(二)原因、解决方案提炼【重点】

tcp粘包(一)

1 沾包(希望分开发送的属于不同语义的包合在一起发了)

1.1 发送端

发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

作者认为说的不够准确,并不会刻意等发送端缓冲区满,而是因为有nagle的存在,让发送端有了等的理由(14读缓冲区(滑动窗口)耗尽与write阻塞、拆包、延迟(二),其实能让发送端等待的不止nagle,比如对端窗口耗尽也是一个),一等,后面的包就搞在一起了,如:

socket缓冲区与沾包 nagle in tcp 中 https://blog.csdn.net/wdscq1234/article/details/52432095 Hello 5个包分开与nagle 《TCP-IP详解:Nagle算法》

netty(十八)《netty粘包消息定长 实践》中,沾包哪里来的?nagle in netty 中手动启用nagle的情况

上两篇文章中,启用nagle的情况下,都发生了沾包,但有一种情况下除外

11读缓冲区(滑动窗口)耗尽与write阻塞、拆包、延迟(一)该文中,未禁用nagle,但由于发送缓冲区==包大小,发送端只能放一个包,即使在等ack的期间,也没跟后面的包搞在一起,因为它们进不来,而且它们会write阻塞

1.2 长连接,如果利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题

作者认为,很简单,短连接所有包属同一个语义结构,后面不会再有包了呗,发送端永远不会沾包,因为没有其它语义结构的包了,接收端更不会沾包,没包可粘

(当然,短连接还是有可能拆包,只不过接收端一次读1个或几个,直到读到-1,组成一个语义结构,无下一个语义的包可粘)

那么http协议是怎么样处理发送端拆包导致服务端何时并包的?http协议直到读到content-length个字节或-1较前者,触发response 29java socket模拟http【重点】springboot json post bug,wireshark抓http包 ;

短链接同样需要类似长连接的结束机制告诉服务器,我传完了,你可以组装并反序列化了,不一定是-1的方式http协议长连接的粘拆包(短链接没有处理沾包的问题,但是会有何时并包的问题,以netty为例)【重点】  【重要】短链接没有处理沾包的问题,但是会有何时并包的问题,任何基于tcp协议的通信,无论长连接短链接都要处理边界,因为tcp协议面向流

而长连接前后把不同语义的包由于nagle搞在一起发

发送端沾包可通过禁用nagle避免,但同时降低了网络效率

14读缓冲区(滑动窗口)耗尽与write阻塞、拆包、延迟(三)证明了这种说法是不完全对的,当对端窗口耗尽,本端就会等,等的过程中,只要发送缓冲区够大,一样会有包搞在一起,导致发送端沾包

1.3  传输需要反序列化的字节流,而不仅仅是字节流 

作者认为,字节流无所谓,发一个读一个保存一个,无需反序列化,无需边界

1.4 接收端

接收方不及时接收缓冲区的包,造成多个包接收——比较常见,即使发送端禁用nagle

比如socket缓冲区与沾包 nagle in tcp 中50%发生概率 & netty(十八)《netty粘包消息定长 实践》中,沾包哪里来的?nagle in netty 中默认禁用nagle的情况

具体说明:接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后

接收端沾包只能通过 、分割符、指定长度、头里塞长度解决

为了避免粘包现象,可采取以下几种措施:

(1)对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;

(2)对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;

(3)由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。

 

以上提到的三种措施,都有其不足之处。

(1)第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。

(2)第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。

(3)第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。

 

只能通过 、分割符、指定长度、头里塞长度解决

2 拆包(按顺序)

2.1 大于内核发送缓冲区大小  socket用户缓冲区、socket内核缓冲区与tcp协议buffer(滑动窗口)的关系(理论)

  在tcp缓冲区大小设置 (实践)中了解到,以100设置为SO_RCV_BUF,系统会放到1100-1400,滑动窗口会影响对端的发送导致拆包,同时发送端的发送缓冲区也会导致拆包,以下3篇文章做了实践

  读缓冲区(滑动窗口)耗尽与write阻塞、拆包、延迟(一)

  写入缓冲区、滑动窗口 对小于mss的包是如何拆包的

 2.1.1 发送缓冲区拆包.    发送缓冲区<包<MSS && 发送缓冲区<对端窗口

2.2 滑动窗口拆包 (小于500的窗口,会先等5s再拆)。    发送缓冲区>包>窗口 && 包<MSS

2.3 包(tcp报文-tcp报头)长度大于MSS(MTU-20 ip header-20 tcp header) MSS MTU,对方窗口>包>MSS && 发送缓冲区>包 大于mss的包是如何拆包的 

2.4 以太网帧的payload大于MTU(一般1500字节)进行ip分片 MSS MTU

2.5 人为拆包——不了解的封装的客户端多次写入,导致拆包,服务端就得处理并包,比如某些傻逼http client关于tcp delayedack实践(三)服务间调用 

2021.7.8

有个疑问,如果发送时没有拆包,一个包发过去,这个包在接收端会不会被分开读取

这个包一定有可能会沾包,比如和后面的包一起被读入用户空间,那么对于它自身而言,它占据滑动窗口一个坑位,发一个ack,读取时是否会被系统拆掉?

原文地址:https://www.cnblogs.com/silyvin/p/11993677.html