TheRiver | blog

You have reached the world's edge, none but devils play past here

0%

nagle算法和延迟确认

标题 来源 完成时间 作者 环境
nagle算法和延迟确认 书籍 20200422 TheRiver kernel-3.10.1

参考

TCP-IP详解:Nagle算法

Linux下TCP延迟确认(Delayed Ack)机制导致的时延问题分析

ESX/ESXi 主机的某些存储阵列可能存在读取或写入性能问题 (1002598)

tcp/ip

延时确认

在许多情况下,TCP并不对每个到来的数据包都返回ACK,利用TCP的累积ACK字段就能实现该功能。累计确认可以允许TCP延迟一段时间发送ACK,以便将ACK和相同方向上需要传的数据结合发送。这种捎带传输的方法经常用于批量数据传输。

主机需求RFC1122指出,TCP实现ACK延迟的时延应小于500ms,实践中时延最大取200ms

delay_ack.png

这是一个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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Return false, if packet can be sent now without violation Nagle's rules:
* 1. It is full sized.
* 2. Or it contains FIN. (already checked by caller)
* 3. Or TCP_CORK is not set, and TCP_NODELAY is set.
* 4. Or TCP_CORK is not set, and all sent packets are ACKed.
* With Minshall's modification: all sent small packets are ACKed.
*/
static inline bool tcp_nagle_check(const struct tcp_sock *tp,
const struct sk_buff *skb,
unsigned int mss_now, int nonagle)
{
return skb->len < mss_now &&
((nonagle & TCP_NAGLE_CORK) ||
(!nonagle && tp->packets_out && tcp_minshall_check(tp)));
}

1: 达到mss大小直接发送

2:是FIN报文

3:TCP_CORK未设置,TCP_NODELAY设置

4:TCP_CORK未设置,所有发送的小报文都收到了确认

nagle.png

每个小报文都要等到所有发送的小报文都收到了确认条件满足,才能发送。遗憾的是,这里构造实验环境没有抓包合并的小报文。

nagle1_1.jpg

每组报文(发送到接收)都是一个rtt时间,113-114ms

Nagle与延迟确认

nale_1.jpg

客户端收到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,这就很有可能是延迟确认导致的

ending

78170022_p0_lit.jpg

----------- ending -----------