说明:本文是最近项目上使用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 ));
读取该选项: g etsockopt(s,IPPRO_TCP,TCP_NODELAY,(char*)& on , &optlen );
默认情况下, 发送数据采用N a gle 算法. Nagle 算法是指发送方发送的数据不会立即发出,而是先放在缓冲区, 等缓存区满了再发出. 发送完一批数据后, 会等待接收方对这批数据的回应,然后再发送下一批数据 。
Nagle 算法适用于发送方需要发送大批量数据, 并且接收方会及时作出回应的场合, 这种算法通过减少传输数据的次数来提高通信效率 。
如果发送方持续地发送小批量的数据, 并且接收方不一定会立即发送响应数据, 那么 Nagle 算法会使发送方运行很慢 。
3.2 TCP_CORK选项
TCP链接的过程中,默认开启Nagle算法,进行小包发送的优化。优化网络传输,兼顾网络延时和网络拥塞。这个时候可以置位TCP_NODELAY关闭Nagle算法,有数据包的话直接发送保证网络时效性。
在进行大量数据发送的时候可以置位TCP_CORK关闭Nagle算法保证网络利用性。尽可能的进行数据的组包,以最大mtu传输,如果发送的数据包大小过小则如果在0.60.8S范围内都没能组装成一个MTU时,直接发送。如果发送的数据包大小足够间隔在0.45内时,每次组装一个MTU进行发送。如果间隔大于0.40.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