tcp延迟确认ack
# 为什么要引入TCP延迟确认?内核如何实现?有哪些相关的算法,其内涵是什么?
可以通过设置socket参数TCP_NODELAY来完全避免延迟确认吗?如何调整或关闭系统性的延迟确认?什么情况下ack会立即发送?
1.为了解决SWS(silly window syndrome,低能窗口综合症),例如在极端情况下,接收窗口只有一个字节,发送方发送一个字节随即被确认,窗口归0,随后接收方读取完一个字节,更新接收窗口为1,发送方继续发送一个字节,由此往复造成了很大的带宽开销,因此引入了延迟确认。 2.linux内核的实现方案是将确认和窗口更新延迟一段时间(redhat定义默认初始值为40ms,随后根据连接的重传超时时间(RTO)、上次收到数据包与本次接收数据包的时间间隔等参数进行不断调整。可通过修改tcp_delack_min,调整系统级别的最小延迟确认时间)希望在此期间能获取一些数据从而免费搭载出去。 3.尽管延迟确认减少了接收端给网络带来的负载,但是发送端发送多个小数据包的方式仍然低效,避免这种用法的一种算法是Nagle算法,其实现思路是: (1)如果包长度达到MSS,则允许发送; (2)如果该包含有FIN,则允许发送; (3)设置了TCP_NODELAY选项,则允许发送; (4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送; (5)上述条件都未满足,但发生了超时(一般为200ms),则立即发送。 Nagle算法只允许一个未被ACK的包存在于网络,它并不管包的大小,因此它事实上就是一个扩展的停-等协议,只不过它是基于包停-等的,而不是基于字节停-等的。Nagle算法完全由TCP协议的ACK机制决定,这会带来一些问题,比如如果对端ACK回复很快的话,Nagle事实上不会拼接太多的数据包,虽然避免了网络拥塞,网络总体的利用率依然很低。使用TCP_NODELAY选项可以禁止Negale 算法。 cork算法: 所谓的CORK就是塞子的意思,形象地理解就是用CORK将连接塞住,使得数据先不发出去,等到拔去塞子后再发出去。设置TCP_CORK选项后,内核会尽力把小数据包拼接成一个大的数据包(一个MTU)再发送出去,然而,由于内核并不知道应用层到底什么时候会发送第二批数据用于和第一批数据拼接以达到MTU的大小,因此内核会给出一个时间限制(一般为200ms),在该时间内没有拼接成一个大包(努力接近MTU)的话,内核就会无条件发送。也就是说若应用层程序发送小包数据的间隔不够短时,TCP_CORK就没有一点作用,反而失去了数据的实时性。 clark: nagle试图解决发送端每次只发送一个字节的问题,而clark则是要解决接收端应用每次只从TCP流中只读取一个字节而引发的问题,其解决方案是禁止接收端发送只有1个字节的窗口更新,相反,它强制接收端必须等待一段时间,知道有了一定数量的可用空间之后再通告给对方。特别是,只有接收端能够处理它在建立连接时宣告的最大数据段,或者它的缓冲区一半为空时(相当于两者之中取较小的值),它才发送窗口更新段。 4.由上述可知,设置TCP_NODELAY选项只能禁用nagle,系统性的延迟确认依然存在 5.2中提到,可以设置tcp_delack_min为1将延迟时间最低降至1ms,另外设置TCP_QUICKACK选项可以立即确认,但该选项不是永久的,必须在每次recv()之后重新设置。 6.linux下socket有一个pingpong属性来表明当前链接是否为交互数据流,如其值为1,则表明为交互数据流,会使用延迟确认机制。但是pingpong这个值是会动态变化的。还有一个quick属性,其代码中的注释为:scheduled number of quick acks,即快速确认的包数量,每次进入quickack模式,quick被初始化为接收窗口除以2倍MSS值,每次发送一个ACK包,quick即被减1。 满足以下两个条件则为QUICK模式,ack会立即发送 1).pingpong被设置为0。 2).快速确认数(quick)必须为非0。