纳格算法
纳格算法是以减少封包发送量来增进TCP/IP网络的性能。它由约翰·纳格任职于Ford Aerospace时命名。
纳格的文档[注 1]描述了他所谓的「小封包问题」-某个应用程序不断地送出小单位的数据,且某些常只占1字节大小。因为TCP封包具有40字节的标头信息(TCP与IPv4各占20字节),这导致了41字节大小的封包只有1字节的可用信息,造成庞大的浪费。这种状况常常发生于Telnet工作阶段-大部分的键盘操作会产生1字节的数据并马上送出。更糟的是,在慢速的网络连接下,这类的封包会大量地在同一时点传输,造成壅塞碰撞。
纳格算法的工作方式是合并(coalescing)一定数量的输出数据后一次送出。特别的是,只要有已送出的封包尚未确认,发送者会持续缓冲封包,直到累积一定数量的数据才送出。
算法
if有新数据要发送
if讯窗大小>= MSS and可发送的数据>= MSS
立刻发送完整MSS大小的segment
else
if管线中有尚未确认的数据
在下一个确认(ACK)封包收到前,将数据排进缓冲区队列
else
立即发送数据
MSS = 最大分段大小
该算法与 TCP延迟确认 会有不好的相互作用,例如当进程发送端进行两次连续的小段写再跟着读时,接收端接收到第一次写后因TCP延迟确认而等待第二次写后一并发送ACK,发送端则因第二次写数据长度小于MSS而等待第一次写的ACK(如上算法所示),最终将导致两对端都进入等待直到ACK延迟超时。因为这个原因,TCP实现通常为应用进程提供一个禁用Nagle算法的接口(通常称为TCP_NODELAY选项)。用户级解决方案是避免套接字上的 写-写-读 串行。 写-读-读 和 写-写-写 都是没问题的。但 写-写-读 则是性能杀手。所以,如果可以的话,缓冲你对TCP的小段写,然后一次发送它们。在每次读之前使用标准的UNIX I/O包并冲刷写缓存通常能起作用。
与延迟 ACK 的交互
该算法与TCP 延迟确认(delayed ACK)的交互很糟糕,该功能在 1980 年代初大致同时引入 TCP,但由不同的组。激活这两种算法后,对 TCP 连接运行两次连续写入,然后在第二次写入的数据到达目的地后才会完成读取的应用进程会经历长达 500 毫秒的持续延迟,“确认延迟”。建议禁用其中任何一个,尽管传统上禁用 Nagle 更容易,因为实时应用进程已经存在这样的开关。
Nagle 推荐的解决方案是通过缓冲应用进程写入然后刷新缓冲区来避免算法发送过早的数据包:
用户级解决方案是避免套接字上的写-写-读串行。写-读-写-读没问题。写-写-写很好。但是写-写-读是一个杀手。因此,如果可以的话,缓冲您对 TCP 的少量写入并一次性发送它们。使用标准 UNIX I/O 包并在每次读取之前刷新写入通常是可行的。
Nagle 认为延迟 ACK 是一个“坏主意”,因为应用层通常不会在时间窗口内响应。对于典型用例,他建议禁用“延迟 ACK”而不是他的算法,因为“快速”ACK 不会像许多小数据包那样产生那么多开销。
禁用 Nagle 或延迟 ACK
TCP 实现通常为应用进程提供一个接口来禁用 Nagle 算法。这通常称为TCP_NODELAY选项。在 Microsoft Windows 上,TcpNoDelay注册表开关决定了默认值。TCP_NODELAY自 1983 年 4.2BSD 中的 TCP/IP 堆栈以来就存在,这是一个具有许多后代的堆栈。
系统间禁用延迟ACK的接口不一致。该TCP_QUICKACK标志自 2001 年 (2.4.4) 起在 Linux 上可用,并且可能在官方界面为SIO_TCP_SET_ACK_FREQUENCY. 默认情况下,在 Windows 注册表中设置TcpAckFrequency为 1 会关闭延迟 ACK。