Nagle算法

   说明:本文是最近项目上使用tcp时遇到的问题找到的原因,参考了网络上的几篇文章整理出来,如有版权问题,请留言。

   Nagle算法用于对缓冲区内的一定数量的消息进行自动连接。该处理过程(称为Nagling),通过减少必须发送的封包的数量,提高了网络应用程序系统的效率。

1. Nagle算法的规则

  (可参考tcp_output.c文件里tcp_nagle_check函数注释):
1)如果包长度达到MSS(MSS是最大分段大小Maxitum Segment Size ,MTU是最大传输单元Maxitum Transmission Unit),则允许发送;
2)如果该包含有FIN,则允许发送;
3)设置了TCP_NODELAY选项,则允许发送;
4)未设置TCP_CORK选项时,若所有发出去的包均被确认,或所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送。
   对于规则4),就是说一个TCP连接上最多只能有一个未被确认的小数据包,在该分组的确认到达之前,不能发送其他的小数据包。如果某个小分组的确认被延迟了,那么后续小分组的发送就会相应的延迟。也就是说延迟确认影响的并不只是被延迟确认的那个数据包,而是后续所有的应答包。

2. Nagle算法的门槛

   实际上Nagle算法并不是很复杂,他的主要职责是数据的累积,实际上有个门槛:

1)缓冲区中的字节数达到了一定量

2)等待了一定的时间(一般的Nagle算法都是等待200ms);

3)紧急数据发送

   个门槛的任何一个达到都必须发送数据了。一般情况下,如果数据流量很大,第二个条件是永远不会起作用的,但当发送小的数据包时,第二个门槛就发挥作用了,防止数据被无限的缓存在缓冲区不是好事情哦。 

3. Nagle算法的选项配置

   TCP_NODELAY和TCP_CORK都是禁用Nagle算法,只不过NODELAY完全关闭而TCP_CORK完全由自己决定发送时机。Linux文档上说两者不要同时设置

3.1 TCP_NODELAY 选项

设置该选项: setsockopt(s,IPPRO_TCP,TCP_NODELAY,(const char*)&on,sizeof(int));
读取该选项: getsockopt(s,IPPRO_TCP,TCP_NODELAY,(char*)&on,&optlen);  

  

   默认情况下, 发送数据采用Nagle 算法. Nagle 算法是指发送方发送的数据不会立即发出,而是先放在缓冲区, 等缓存区满了再发出. 发送完一批数据后, 会等待接收方对这批数据的回应,然后再发送下一批数据 

   Nagle 算法适用于发送方需要发送大批量数据, 并且接收方会及时作出回应的场合, 这种算法通过减少传输数据的次数来提高通信效率

   如果发送方持续地发送小批量的数据, 并且接收方不一定会立即发送响应数据, 那么Nagle算法会使发送方运行很慢

3.2 TCP_CORK选项

   TCP链接的过程中,默认开启Nagle算法,进行小包发送的优化。优化网络传输,兼顾网络延时和网络拥塞。这个时候可以置位TCP_NODELAY关闭Nagle算法,有数据包的话直接发送保证网络时效性。

   在进行大量数据发送的时候可以置位TCP_CORK关闭Nagle算法保证网络利用性。尽可能的进行数据的组包,以最大mtu传输,如果发送的数据包大小过小则如果在0.6~0.8S范围内都没能组装成一个MTU时,直接发送。如果发送的数据包大小足够间隔在0.45内时,每次组装一个MTU进行发送。如果间隔大于0.4~0.8S则,每过来一个数据包就直接发送。

   Nagle组织包的长度是由系统决定的,有时候我们知道我们会每个1分钟产生1字节,共1000字节。如果完全由Nagle算法来发送的话,可能还是会1字节1字节发送[这是一种极端情况,假设返回ACK时间不是很长]。这个时候首先设置TCP_CORK能够阻塞住TCP[尽量阻塞住],等我们write完1000字节之后,取消TCP_CORK,这个时候就能够将1000字节一次发出

总结:

   TCP_CORK选项与TCP_NODELAY一样,是控制Nagle化的。      

1)打开TCP_NODELAY选项,则意味着无论数据包是多么的小,都立即发送(不考虑拥塞窗口)。
2)如果将TCP连接比喻为一个管道,那TCP_CORK选项的作用就像一个塞子。            

   设置TCP_CORK选项,就是用塞子塞住管道,而取消TCP_CORK选项,就是将塞子拔掉。            

   当TCP_CORK选项被设置时,TCP链接不会发送任何的小包,即只有当数据量达到MSS时,才会被发送

   一般当数据传输完成时,通常需要取消该选项,以防被塞住,这样才可以让不够MSS大小的包能及时发出去。

例如下面这段代码:

  1:  int on = 1;

  2:  setsockopt(sockfd, SOL_TCP, TCP_CORK, &on, sizeof(on)); //set TCP_CORK

  3:  write(sockfd, ...);    //e.g., http header

  4:  sendfile(sockfd, ...); //e.g., http body

  5:  on = 0;

  6:  setsockopt(sockfd, SOL_TCP, TCP_CORK, &on, sizeof(on)); //unset TCP_CORK

你可能感兴趣的:(Nagle算法)