[tcp/ip] tcp delayed ack
触发机制
被delay的ack包,什么时候发送出来?
- 到达500ms (这个值根据实现的不同而不同,RFC要求最多不能超过500,linux的实现是200)
- 每两个数据包回一个ack
- 由反向数据包发送带回。
优点
delayed ack可以提供一个机会给应用程序。让三个回应报合并成一个回应包。
三个分别为:ack,窗口更新,应用层回应。
应用层回应显然是应用程序主动发回的。窗口更新也跟应用层有关,是因为应用程序从buffer里读走了数据,窗口自然会发生更新。
所以,ack延迟500毫秒,最大的好处是为了让这三个信息从一个包里携带过去。
缺点
write write read 问题
当delayed ack和nagle算法同时使用时,会引起write write read问题。 这个问题的本质是,传输层因为启用了这两个算法,而导致了应用层在等待传输层的问题。
举例:A与B通信,a发送1和2然后读取,b收到1和2后发送他们的和3给a,a最终收到1和2的和3.
这个场景的前提是1已经发出去了。然后a发送2并堵塞读。由于2已经交付给了操作系统。所以应用程序a便阻塞在了读操作上面。
这个时候,应1的ack还没有回来。所以a所在的操作系统因为nagle算法并不会把2发出去。
b所在的操作系统收到了1并交付给了应用程序b。但是b还没有收到加法的第二个操作数2,所以也不会主动发送他们的和3.
这个是a和b所在的两个tcp协议栈就在相互等待对方,而导致原本可应该更快处理的两个应用层程序出现了堵塞。a在等待1的ack,b在等待第二个操作数2。
这个僵局直到,b的delay ack到达500毫秒的超时发送了1的ack才会被打破。
这就是 write write read问题。
慢启动
TCP慢启动是为了以指数增长的趋势快速逼近最大带宽。很显然,在这个前提下,delayed ack会使逼近速度降低。
所以在慢启动过程中,快速确认(quick ack)也是被启动的。linux里有这样一个宏定义的值,来达到这个目的
/* Maximal number of ACKs sent quickly to accelerate slow-start. */ #define TCP_MAX_QUICKACKS 16U
配置方法
linux下,使用setsockopt()可以对这两个特性,delayed ack和nagle算法进行配置。
- nagle: 选项 TCP_NODELAY 用来控制发包侧的行为。
- delayed ack: 选项 TCP_QUICKACK 用来控制收包侧的行为。
- 值得一提的是,delay ack与quick ack(快速确认)直接的转换是协议栈动态调节的,即使在同一条TCP连接里,显式设置的该选项
也会被OS在恰当的时机里调节回去。
参考:man 7 tcp