Nagle 算法
Nagle算法由John Nagle在1984年提出,这个算法可以减少网络中小的packet的数量,从而降低网络的拥塞程度。一个常见的例子就是Telnet程序,用户在控制台的每次击键都会发送一个packet,这个packet通常包含41个字节,然而只有一个字节是payload,其余40个字节都是header,如果每次击键都发送一个packet,那就会造成了巨大的开销。
为了减小这种开销,Nagle算法指出,当TCP发送了一个小的segment(小于MSS),它必须等到接收了对方的ACK之后,才能继续发送另一个小的segment。那么在等待的过程中(一个RTT时间),TCP就能尽量多地将要发送的数据收集在一起,从而减少要发送的segment的数量。
默认情况下,TCP开启了Nagle算法,然而Nagle算法并不是灵丹妙药,它会增加TCP发送数据的延迟。在一些要求低延迟的应用程序中(例如即时通讯应用),则需要禁用Nagle算法:
Delayed ACK
TCP的 Delayed ACK 与Nagle算法有异曲同工之妙,Delayed ACK很好理解,当TCP接收到数据时,并不会立即发送ACK给对方,相反,它会等待应用层产生数据,以便将ACK和数据一起发送(在Linux最多等待40ms)。
我们知道,Nagle算法会增加TCP发送数据的延迟,然而,在某些情况下,Delayed ACK会放大这种延迟。一个常见的例子客户端HTTP POST协议,首先看看客户端的代码:
客户端调用两次write()
来发送HTTP POST请求,服务端则需要调用两次read()
读取客户端的HTTP请求:
通常来说,HTTP请求的header和body都是小的segment,但由于TCP默认开启Nagle算法,因此客户端在发送请求的header之后,如果还未收到ACK,则不能发送body。
Server在收到请求的header之后,由于还没有收到请求的body,因此无法立即产生HTTP响应给客户端,这就导致了http_request_header的ACK大概会延迟40ms才发送。
为避免这种延迟的出现,需要做两件事:
- 设置
TCP_NODELAY
选项。 - 将客户端的两次
write()
合并成一个,避免服务端的Delayed ACK。