标题 | 来源 | 完成时间 | 作者 | 环境 |
---|---|---|---|---|
nagle算法和延迟确认 | 书籍 | 20200422 | TheRiver | kernel-3.10.1 |
参考
Linux下TCP延迟确认(Delayed Ack)机制导致的时延问题分析
ESX/ESXi 主机的某些存储阵列可能存在读取或写入性能问题 (1002598)
延时确认
在许多情况下,TCP并不对每个到来的数据包都返回ACK,利用TCP的累积ACK字段就能实现该功能。累计确认可以允许TCP延迟一段时间发送ACK,以便将ACK和相同方向上需要传的数据结合发送。这种捎带传输的方法经常用于批量数据传输。
主机需求RFC1122指出,TCP实现ACK延迟的时延应小于500ms,实践中时延最大取200ms
这是一个ssh连接,客户端依次输入date \n 后抓到的报文。可以看到:
frame.number = 5 client send d to server
frame.number = 6 server send ack to client
frame.number = 7 senver send d to client(回显)
frame.number = 8 clinet send ack to server
frame.number = 18 client send t to server
frame.number = 19 server send ack and t(回显) to client
frame.number = 20 clinet send ack to server
4步是普通的报文,3步的是延迟确认。这里由于延迟很小,所以延迟确认回复比较快,体现出来只是报文数变少了。如果frame.number = 6等7号报文很久没等到,则客户端要一直等到服务端收到7号报文后才会发出ack确认(延迟确认没超时的情况下)
Nagle算法
Nagle算法要求,当一个TCP连接中有在传数据(即那些已发送但还未经确认的数据,长度小于mss)就不能被发送,直到所有的在传数据都收到ack,并且在收到ack后,将这些小包整合起来一起发送。
直观的说,Nagle就是在前面的小报文未被确认的时候,后面的小报文先不发送,直到收到前面小报文的ack了,把后面积累的小报文一起发送出去,这样就减少了小报文的传输。这些小报文有效负荷很低,每次都传输有点浪费带宽。
1 | /* Return false, if packet can be sent now without violation Nagle's rules: |
1: 达到mss大小直接发送
2:是FIN报文
3:TCP_CORK未设置,TCP_NODELAY设置
4:TCP_CORK未设置,所有发送的小报文都收到了确认
每个小报文都要等到所有发送的小报文都收到了确认
条件满足,才能发送。遗憾的是,这里构造实验环境没有抓包合并的小报文。
每组报文(发送到接收)都是一个rtt时间,113-114ms
Nagle与延迟确认
客户端收到2个服务端发来的报文,由于延迟确认没有即刻回复ack.服务端由于Nagle算法,要等收到前面报文的ack才继续发送,于是导致了死锁。在延迟确认超时前一直阻塞。
总结
- linux禁用延迟确认可以setsockopt函数设置TCP_QUICKACK
- linux禁用Nagle可以设置TCP_NODELAY
- 延迟确认和Nagle算法碰到一起可能导致死锁,需要谨慎
- tcp.analysis.ack_rtt > 0.2 and tcp.len == 0 过滤超过200ms的确认
得分情况,看是别人出发晚还是路上耽搁久,服务器如果是发了一条数据,然后等了>0.2s后才发的ack,这就很有可能是延迟确认导致的