上一期《准确追踪故障原因:掌握TCP时间戳的分析方法》我们介绍了TCP的时间戳选项,接下来我们再聊聊延迟确认与Nagle算法,继续围绕时序图和大家探讨TCP相关内容。
本期互动问题,欢迎大家评论区一起聊聊:
有关延迟确认与Nagle两种机制碰撞,你遇到过哪些故障?后来是怎样解决的?
如果网络中的小包过多,会怎样?
试想一下,如果网络中的充满了有效载荷1字节或者10字节的小包,那么传输效率明显是很低的,这相当于利用渣土货车运输一箱牛奶,造成了极大的网络资源浪费。因此,延迟确认机制和Nagle算法被设计用于减少网络中小包的出现。
延迟确认(Delayed ACK)和Nagle算法都是TCP协议中用于优化网络小包的重要机制,它们各自有着独特的功能和作用。本文将围绕时序图和大家探讨延迟确认和Nagle算法相关内容。
什么是延迟确认?
延迟确认(Delayed ACK)在接收数据时生效,是TCP协议中的一种优化技术,它的主要目的是减少小包传输场景下的ACK包数量,从而提高网络利用率和性价比。
延迟确认的工作机制为,接收方在收到数据包后,并不会立即发送ACK确认包,而是等待一段时间(通常是200ms),以便更多的小包到达,进行统一的累积确认。如果在200ms时间内没有其他数据包到达,本端也没有应用数据发送的需要,则发送一个单独的ACK累积确认包。这种机制可以减少网络中的ACK包数量,降低网络负载。
简单来说,延迟确认是一种“等一会儿,积攒一些其它数据一起确认”的机制。延迟确认“等”的时间上限为500ms,字节长度上限为1个MSS,一般情况下延迟确认机制在Windows系统实现的默认时间为200ms,在Linux系统实现的默认时间为40ms,关于这些数值,可以通过修改注册表
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{当前网卡GUID}\TcpDelAckTicks
以及系统文件/proc/sys/net/ipv4/tcp_delack_min修改。
下图展示了CSNA系统在分析Windows系统延迟确认流量时的情况:

图1 Windows系统启用延迟确认的情况
如图1所示,通过三次握手RTT可以测得客户端和服务器之间的网络时延小于1ms,在客户端发送4号HTTP GET数据包后,服务器进入延迟确认机制,暂不回应ACK;
208ms后,服务器回应了5号包,即针对4号包的传输层ACK响应;又过了824ms,服务器已完成客户端4号包应用请求的处理,通过6号HTTP 200 OK包对4号包进行应用层响应;
如果通过ACK时延方式,计算4、5号包之间的ACK时延,得到结果为服务器一侧时延为208ms,如果通过三次握手RTT方式,计算三次握手时延,则服务器一侧时延小于1ms;
这两种计算结果严重不符,恰逢4、5号包的时延在200ms这一默认延迟确认数值左右,因此可以判断服务器一侧启用了延迟确认机制。
延迟确认在哪里操作开启或关闭?本例中服务器为Windows系统,通过修改注册表中:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\{当前网卡GUID}\TcpAckFrequency
这一DWORD值即可控制延迟确认的开启或关闭,其中0、2为开启,1为关闭。将本例中服务器的该值改为1后,延迟确认关闭,服务器ACK无需延迟,再次抓包观察,服务器ACK时延和三次握手时延相近,如下图所示:

图2 未启用延迟确认的情况
需要注意的是,延迟确认机制与PSH是冲突的,当TCP延迟确认的接收方突然需要发送PSH数据时,延迟确认会被取消,转而立即进行确认。因此,图1和图2的现象均有一个前提,需要服务器网站程序确实花费长达1秒的时间应用请求,给延迟确认留出时间。如果服务器快速进行应用响应,那么应用响应(HTTP 200 OK)一般都是携带PSH位的,这样就没有延迟确认登场的机会了。本例中为了模拟服务器的缓慢响应情况,笔者在服务器的网页侧增加了sleep函数,要求服务器延迟1秒进行响应,托sleep函数的福,我们才能清晰看到延迟确认的现象。
关闭sleep函数后,服务器快速响应,则PSH优先于延迟确认,如下图所示:

图3 服务器启用了延迟确认,但需要发送PSH的情况
如图3所示,服务器花费90ms处理客户端HTTP请求,并返回了HTTP 200 OK数据包,该数据包携带PSH位,则忽略延迟确认,优先发送携带PSH的应用响应。
总的来说,延迟确认机制能够节约接收方ACK包的数量,尤其是在发送方(可能是客户端或服务器的任意一方)连续发送不含PSH位的小包的场景下非常有效。
什么是Nagle算法?
延迟确认是通过限制接收方发送ACK来减少小包数量的,再来看看另一种限制方法:Nagle算法。它由John Nagle提出,用于限制发送方一次能够发送的小包数量,从而提高网络效率。Nagle算法的核心思想是,在一个TCP连接上,最多只能有一个未被确认的小数据包(小于MSS)。如果发送方有多个小数据包需要发送,则先缓存,直到累积到足够的数据量或者接收到之前发送的数据包的ACK后再一起发送。Nagle算法通过减少网络中发送方小数据包数量,降低网络拥塞的可能性,提高网络吞吐量。
简单来说,Nagle算法是一种“等一会儿,积攒一些其它数据一起发”的机制。这里“攒”的字节长度上限为1个MSS,通常是1460字节数据,“等”的时间上限通常是200ms。Nagle算法在Windows和Linux系统中均默认启用。
笔者模拟了一个场景,服务器启用任意端口服务(这里以telnet为例),客户端通过Python程序实现telnet连接建立后,客户端发送用户名时,连续发送5个10字节载荷数据的小包。模拟实现的流量效果如下:

图4 连续5个10字节载荷长度的小包场景(禁用Nagle算法)
如图4所示,客户端发送了8、9、10、12、14号这5个小包。看起来小包有些多,不过这里还没有启用Nagle算法,接下来将代码中的Nagle算法启用,再进行观察:

图5 连续5个10字节载荷长度的小包场景(启用Nagle算法)
如图5所示,启用Nagle算法后,客户端发出第一个10字节的包后,边等待服务器的ACK边“攒”数据,待服务器回复ACK后,将剩余40字节通过一个数据包发出。
Nagle算法如何启用?当前,Windows系统和Linux系统均默认启用Nagle算法,具体关闭方法可通过修改注册表和系统文件进行关闭。
总的来说,Nagle算法能够减少连续小包场景下的小包数量,但由于需要等到ACK再继续发送数据,这种机制也可能导致数据传输的延迟增加,特别是在交互式应用中。
当延迟确认遇见Nagle算法,会发生什么?
既然两种机制都是用于减少小包数量的,那能不能同时打开,让小包数量少上加少,达到更好的效果?先说答案:发送方和接收方不能同时启用这两种机制,否则可能会导致会话短暂死锁。
当延迟确认和Nagle算法同时作用于TCP连接时,发送方使用Nagle算法缓存了小数据包,并等待接收方的ACK后继续发送,如果恰好接收方启用了延迟确认机制,将导致会话死锁在延迟确认的时间中。好在这种死锁能够在延迟确认到期后自动解除,但这也将导致数据传输延迟显著增加,严重影响网络效率。
下图展示了发送方(客户端)启用Nagle,接收方(服务器)启用延迟确认的情况:

图6 延迟确认撞上Nagle
图6中展示的仍然是图4和图5的场景,只是增加了延迟确认和Nagle。他们的交互过程变成了这样:
- 1. 客户端发送一个数据包(10号包,携带10字节数据),此时Nagle生效,客户端暂停发送,开始积攒待发送的其它数据,等到客户端发送ACK确认后再继续发送;
- 2. 服务器收到10号包后,启动延迟确认机制,开始进行等待,在等待期间,由于客户端未继续发送数据,因此服务器未凑够1个MSS的数据,且无PSH数据需要立即发送,因此足足等待200ms延迟确认结束(图中实际为195ms)后,发送11号ACK包给客户端;
- 3. 客户端终于收到了来自服务器的确认,Nagle继续生效,客户端再次发送一个数据包(12号数据包,包含本来应该发送的10字节数据和已经积攒的30字节数据),并继续暂停发送、积攒数据,等待服务器发送ACK;
- 4. 服务器收到12号数据包后,启动延迟确认机制,开始进行等待,再次足足等待200ms延迟确认结束(图中实际为202ms),发送13号ACK包给客户端。
经过两轮往复,约400ms的时间已经被延迟确认和Nagle的死锁浪费掉了……如果是多次交互的情况,结果可想而知,每次往返都需要等待一个200ms,时间代价太大。
如何解决此类问题?
实际上,笔者搜索了诸多网络资料,也没能看到一起实际生产环境中由于Nagle和延迟确认导致的此类故障,因为触发这种流量的条件过于苛刻,第一是“等”或“攒”的数据长度不能超过1个MSS,第二是应用响应速度要够慢,等待期间不能出现需要PSH位立即发送数据的情况。因此,本文中的故障流量也是通过python程序模拟发送的。
不过为了避免这种情况的发生,可以采取以下措施:
1.禁用Nagle算法:使发送方不再等待接收方的ACK,而是立即发送数据包,防止遇到对端接收数据启用延迟确认的情况。
2.禁用延迟确认:使接收方不再等待发送方的其他数据,而是立即发送ACK包,防止遇到对端发送数据启用Nagle算法的情况。
总之,关闭这两个算法,可以避免此类问题发生。
本文介绍了TCP的延迟确认和Nagle算法,深入探讨了两种机制的原理和流量现象,包括了两种机制互相碰撞时引发的故障,以及在CSNAS时序图中对于此类机制的分析方法,通过深入理解和掌握此类分析技巧,能够提升流量分析工程师在工作中快速分析解决此类故障的能力。
还记得开篇的互动问题吗?
有关延迟确认与Nagle两种机制碰撞,你遇到过哪些故障?后来是怎样解决的?