zoukankan      html  css  js  c++  java
  • TCP系列29—窗口管理&流控—3、Nagle算法

    一、Nagle算法概述

    之前我们介绍过,有一些交互式应用会传递大量的小包(称呼为tinygrams),这些小包的负载可能只有几个bytes,但是TCP和IP的基本头就有40bytes,如果大量传递这种小包,会严重降低网络利用率,还可能造成网络拥塞。福特公司就曾经遇到过这种问题,John Nagle提出了一种通过ACK报文控制TCP发包的方法解决了这种问题,这种方法也就以Nagle名字命名,称为Nagle算法。Nagle算法最开始的标准为RFC896,但是RFC896目前已经被RFC7805移动到了Historic状态,原因是RFC1122和RFC6633描述的内容已经取代了RFC896。

    二、Nagle算法概述

    用一句话简单的描述Nagle算法就是:在任意时刻,最多只能有一个未被ACK确认的小包

    按照RFC1122描述,如果有发送出去的但是还没有被ACK确认的数据,那么发送方应该缓存所有的用户数据,直到发出去的数据被ACK确认或者缓存的TCP数据可以发出一个full-sized的报文。full-sized报文一般是指达到了SMSS的报文。这样实际上按照这样的要求,对于小包的处理,TCP实际上进入了停等式(stop-and-wait)。这里的协议描述并没有限制发送出去还未确认的报文是否为小包,而在linux实现上则会进一步判断如果有发出去的还没确认的小包(小于SMSS的报文)才会进一步判断是否使能Nagle算法(请参考wireshark示例)。

    RFC1122还说明一个TCP主机应该实现Nalge算法来把小包汇聚,但是一定要实现一个接口可以让应用层单独在一个TCP连接上禁用Nagle算法。linux中Nagle算法默认是打开的,应用程序可以通过TCP_NODELAY选项来设置socket关闭Nagle算法(注意是关闭)。

    三、Nagle算法与延迟ACK

    当Nagle算法和延迟ACK同时使能的时候,可能会造成发送端等待ACK确认包以发送剩下的小包数据,而接收方等待应用层数据或者等待接收新的数据以超过一个接收MSS后在发送ACK确认包。这样发送发和接收方互相等待对方(称呼为deadlock),最终延迟ACK超时。这种情况下就会提升平均时延,降低网络性能,因此可能需要禁用Nagle算法或者延迟ACK算法。参考下面的wireshark示例。

    四、wireshark示例

    在下面的相关测试中需要关闭网卡的TSO和GSO功能,关闭方法

    1. ethtool -K lo tso off
    2. ethtool -K lo gso off

    查看网卡TSO和GSO相关配置可以通过下面命令查看

    1. ethtool -k lo

    TSO和GSO功能是指TCP模块发送报文的时候,可以以整数倍的MSS大小发送,然后网卡进行分段,最终发出去的报文是没有超过MSS的,但是wireshark在TSO之前抓包,看到的数据包的大小会为MSS的整数倍。例如下面示例1如果不关闭TSO和GSO功能,则第一次write写入105bytes的数据后,wireshark会显示先发送了100bytes的数据,然后又发送了5bytes的数据。

    另外下面示例图中,除了TLP和Nagle交互的示例外,其余示例的截图中的高亮的数据包是我为了解释标记出来的,并不是wireshark标记的异常包。

    1、默认打开Nalge算法场景

    如下图所示,server与client建立连接后,client发送一个No4数据包,server回复ACK确认,连接正常建立。其中在No1报文中可以看到client端通告的MSS选项中MSS为62bytes。server端扣除TSopt选项的12bytes后(其中还包括两个nop选项),最大只能发送50bytes的数据。接着server端连续两次write写入,第一次写入105bytes,第二次写入58bytes。可以看到在以MSS大小发出去No6和No7报文之后,剩余的5bytes同样也发出去了,并没有因为之前有未ACK确认的full-sized数据包而阻止发送。接着第二次写入58byte后,其中前50bytes可以构成一个full-sized包,因此可以直接发送出去,但是剩余的8bytes因为不能构成一个full-sized报文,同时还没有收到前面No8这个小包的ACK确认包,因此这8bytes会暂时被Nagle算法阻止发送,直到收到No8的确认包No12后,最后的8bytes才得以传输出去。

    2、设置关闭Nagle场景

    同样运行上面的测试用例,但是这次server端代码中通过TCP_NODELAY设置关闭Nagle算法。结果如下图,与上面示例对比,这里不再额外解释。

    3、Nagle算法和延迟ACK同时打开的场景

    client与server端建立连接后先写入20个数据包,每个包的长度为58bytes,接着client每隔3ms写入3bytes的数据,最终结果如下图所示

    之前我们已经介绍过一般连接建立后第36个数据包以后才会启动延迟ACK,从下图可以看到No36的确认包No37确认发生了延迟。但是No38的确认包并没有延迟,原因是No39需要更新窗口(对比图中No39和No37的Win字段)。No40以后可以看到基本上每次server端回复ACK都需要延迟等待大约40ms,而client受限与nagle算法只能等到server端回复ACK确认包后才能发出新的数据。可以看到这个时候,client端每个数据包的平均延迟被将会变大,而且延迟ACK的延迟时间ato还会动态调整(一般最大不超过RTO),所以对于时延比较敏感的交互式应用应该关闭Nalge算法。

    在打开Nagle算法的场景下,测试完成后wireshark显示总共交换了87个数据包,总耗时1.004s,关闭Nagle算法后进行同样的测试,wireshark显示总共交换了361个数据包,总耗时0.967s。另外从应用层看每个数据包的平均耗时打开Nagle算法和关闭Nagle算法对比,将会更长。关闭Nagle算法测试大的wireshark抓包文件可以自行下载查看,此处不再赘述。

    4、Nagle和TLP交互

    之前我们介绍TLP的时候,曾经提到过TLP的Loss Probe探测包并不受到Nagle的约束,示例如下,No10为TLP的Loss Probe报文,可以看到并没有受到Nagle算法的约束。


    补充说明:

    1、windows下面可以通过添加DWORD注册表项HKLM/SOFTWAREMisrosoftMSMQParametersTCPNoDelay全局关闭Nagle算法。






  • 相关阅读:
    Leetcode 50.Pow(x,n) By Python
    Leetcode 347.前K个高频元素 By Python
    Leetcode 414.Fizz Buzz By Python
    Leetcode 237.删除链表中的节点 By Python
    Leetcode 20.有效的括号 By Python
    Leetcode 70.爬楼梯 By Python
    Leetcode 190.颠倒二进制位 By Python
    团体程序设计天梯赛 L1-034. 点赞
    Wannafly挑战赛9 C-列一列
    TZOJ Start
  • 原文地址:https://www.cnblogs.com/lshs/p/6038641.html
Copyright © 2011-2022 走看看