网络-Nagel算法&Delay ACK

传输一个字节的数据需要20字节的TCP头部和20字节的IP头部,Nagle就提出了一种通过减少需要通过网络发送包的数量来提高TCP/IP传输的效率。

Nagel算法

if有新資料要傳送
   if訊窗大小>= MSS and可傳送的資料>= MSS
     立刻傳送完整MSS大小的segment
   else
    if管線中有尚未確認的資料
      在下一個確認(ACK)封包收到前,將資料排進緩衝區佇列
    else
      立即傳送資料 

MSS = 最大分段大小

Delay ACK

TCP是可靠传输,可靠的核心是收到包后回复一个ack来告诉对方收到了。

delay ack是指收到包后不立即ack,而是等一小会(比如40毫秒)看看,如果这40毫秒以内正好有一个包(比如上面的http response)发给client,那么我这个ack包就跟着发过去(顺风车,http reponse包不需要增加任何大小),这样节省了资源。 当然如果超过这个时间还没有包发给client(比如nginx处理需要40毫秒以上),那么这个ack也要发给client了(即使为空,要不client以为丢包了,又要重发http request,划不来)。

假如这个时候ack包还在等待延迟发送的时候,又收到了client的一个包,那么这个时候server有两个ack包要回复,那么os会把这两个ack包合起来立即回复一个ack包给client,告诉client前两个包都收到了。

也就是delay ack开启的情况下:ack不立即发而是等40毫秒,等的过程中ack包有顺风车就搭;或者如果凑够两个ack包自己包个车也立即发车;再如果等了40毫秒以上也没顺风车,那么自己打个车也发车。

缺陷及避免

该算法与 TCP延迟确认 会有不好的相互作用,例如当程序发送端进行两次连续的小段写再跟着读时,接收端接收到第一次写后因TCP延迟确认而等待第二次写后一并发送ACK,发送端则因第二次写数据长度小于MSS而等待第一次写的ACK(如上算法所示),最终将导致两对端都进入等待直到ACK延迟超时造成死锁。因为这个原因,TCP实现通常为应用程序提供一个禁用Nagle算法的接口(通常称为TCP_NODELAY选项)。

系统上禁用

linux提供了TCP_NODELAY的选项来禁用Nagle算法。禁用方法:

setsockopt(client_fd, SOL_TCP, TCP_NODELAY,(int[]){1}, sizeof(int));

用户解决方案

RFC指出,用户级解决方案是避免套接字上的 写-写-读 序列。 写-读-读 和 写-写-写 都是没问题的。但 写-写-读 则是性能杀手。所以,如果可以的话,缓冲你对TCP的小段写,然后一次发送它们。在每次读之前使用标准的UNIX I/O包并冲刷写缓存通常能起作用。

Table of Contents