国内最全IT社区平台 联系我们 | 收藏本站
华晨云阿里云优惠2
您当前位置:首页 > 互联网 > Nagle算法

Nagle算法

来源:程序员人生   发布时间:2016-08-06 09:17:17 阅读次数:2583次

用于自动连接许多的小缓冲器消息;这1进程(称为nagling)通过减少必须发送包的个数来增加网络软件系统的效力

优 点
减少堵塞控制

用 于
自动连接许多的小缓冲器消息

简介

Nagle算法是以他的发明人John Nagle的名字命名的,它用于自动连接许多的小缓冲器消息;这1进程(称为nagling)通过减少必须发送包的个数来增加网络软件系统的效力。Nagle算法于1984年定义为福特航空和通讯公司IP/TCP堵塞控制方法,这使福特经营的最早的专用TCP/IP网络减少堵塞控制,从那以后这1方法得到了广泛利用。Nagle的文档里定义了处理他所谓的小包问题的方法,这类问题指的是利用程序1次产生1字节数据,这样会致使网络由于太多的包而过载(1个常见的情况是发送真个”胡涂窗口综合症(Silly Windw Syndrome)”)。从键盘输入的1个字符,占用1个字节,可能在传输上造成41字节的包,其中包括1字节的有用信息和40字节的首部数据。这类情况转变成了4000%的消耗,这样的情况对轻负载的网络来讲还是可以接受的,但是重负载的福特网络就受不了了,它没有必要在经过节点和网关的时候重发,致使包丢失和妨碍传输速度。吞吐量可能会妨碍乃至在1定程度上会致使连接失败。Nagle的算法通常会在TCP程序里添加两行代码,在未确认数据发送的时候让发送器把数据送到缓存里。任何数据随后继续直到得到明显的数据确认或直到攒到了1定数量的数据了再发包。虽然Nagle的算法解决的问题只是局限于福特网络,但是一样的问题也可能出现在ARPANet。这类方法在包括因特网在内的全部网络里得到了推行,成了默许的履行方式,虽然在高互动环境下有些时候是没必要要的,例如在客户/服务器情形下。在这类情况下,nagling可以通过使用TCP_NODELAY 套接字选项关闭。

算法

TCP/IP协议中,不管发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。为了尽量的利用网络带宽,TCP总是希望尽量的发送足够大的数据。(1个连接会设置MSS参数,因此,TCP/IP希望每次都能够以MSS尺寸的数据块来发送数据)。Nagle算法就是为了尽量发送大块数据,避免网络中充斥着许多小数据块。

Nagle算法的基本定义是任意时刻,最多只能有1个未被确认的小段。 所谓”小段”,指的是小于MSS尺寸的数据块,所谓”未被确认”,是指1个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。

Nagle算法的规则(可参考tcp_output.c文件里tcp_nagle_check函数注释):

(1)如果包长度到达MSS,则允许发送;

(2)如果该包括有FIN,则允许发送;

(3)设置了TCP_NODELAY选项,则允许发送;

(4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;

(5)上述条件都未满足,但产生了超时(1般为200ms),则立即发送。

Nagle算法只允许1个未被ACK的包存在于网络,它其实不管包的大小,因此它事实上就是1个扩大的停-等协议,只不过它是基于包停-等的,而不是基于字节停-等的。Nagle算法完全由TCP协议的ACK机制决定,这会带来1些问题,比如如果对端ACK回复很快的话,Nagle事实上不会拼接太多的数据包,虽然避免了网络堵塞,网络整体的利用率仍然很低。

Nagle算法是silly window syndrome(SWS)预防算法的1个半集。SWS算法预防发送少许的数据,Nagle算法是其在发送方的实现,而接收方要做的是不要通告缓冲空间的很小增长,不通知小窗口,除非缓冲区空间有显著的增长。这里显著的增长定义为完全大小的段(MSS)或增长到大于最大窗口的1半。

注意:BSD的实现是允许在空闲链接上发送大的写操作剩下的最后的小段,也就是说,当超过1个MSS数据发送时,内核先顺次发送完n个MSS的数据包,然后再发送尾部的小数据包,其间不再延时等待。(假定网络不阻塞且接收窗口足够大)

举个例子,比如之前的blog中的实验,1开始client端调用socket的write操作将1个int型数据(称为A块)写入到网络中,由于此时连接是空闲的(也就是说还没有未被确认的小段),因此这个int型数据会被马上发送到server端,接着,client端又调用write操作写入’\r\n’(简称B块),这个时候,A块的ACK没有返回,所以可以认为已存在了1个未被确认的小段,所以B块没有立即被发送,1直等待A块的ACK收到(大概40ms以后),B块才被发送。全部进程如图所示:

这里还隐藏了1个问题,就是A块数据的ACK为何40ms以后才收到?这是由于TCP/IP中不单单有nagle算法,还有1个TCP确认延迟机制 。当Server端收到数据以后,它其实不会马上向client端发送ACK,而是会将ACK的发送延迟1段时间(假定为t),它希望在t时间内server端会向client端发送应对数据,这样ACK就可以够和应对数据1起发送,就像是应对数据捎带着ACK过去。在我之前的时间中,t大概就是40ms。这就解释了为何’\r\n’(B块)总是在A块以后40ms才发出。

固然,TCP确认延迟40ms其实不是1直不变的,TCP连接的延迟确认时间1般初始化为最小值40ms,随后根据连接的重传超时时间(RTO)、上次收到数据包与本次接收数据包的时间间隔等参数进行不断调剂。另外可以通过设置TCP_QUICKACK选项来取消确认延迟。

  1. TCP_NODELAY 选项

默许情况下,发送数据采取Nagle 算法。这样虽然提高了网络吞吐量,但是实时性却下降了,在1些交互性很强的利用程序来讲是不允许的,使用TCP_NODELAY选项可以制止Nagle 算法。

此时,利用程序向内核递交的每一个数据包都会立即发送出去。需要注意的是,虽然制止了Nagle 算法,但网络的传输依然遭到TCP确认延迟机制的影响。

  1. TCP_CORK 选项

所谓的CORK就是塞子的意思,形象地理解就是用CORK将连接塞住,使得数据先不发出去,等到拔去塞子后再发出去。设置该选项后,内核会尽力把小数据包拼接成1个大的数据包(1个MTU)再发送出去,固然若1定时间后(1般为200ms,该值尚待确认),内核依然没有组合成1个MTU时也必须发送现有的数据(不可能让数据1直等待吧)。

但是,TCP_CORK的实现可能其实不像你想象的那末完善,CORK其实不会将连接完全塞住。内核其实其实不知道利用层到底甚么时候会发送第2批数据用于和第1批数据拼接以到达MTU的大小,因此内核会给出1个时间限制,在该时间内没有拼接成1个大包(努力接近MTU)的话,内核就会无条件发送。也就是说若利用层程序发送小包数据的间隔不够短时,TCP_CORK就没有1点作用,反而失去了数据的实时性(每一个小包数据都会延时1定时间再发送)。

  1. Nagle算法与CORK算法区分

Nagle算法和CORK算法非常类似,但是它们的着眼点不1样,Nagle算法主要避免网络由于太多的小包(协议头的比例非常之大)而堵塞,而CORK算法则是为了提高网络的利用率,使得整体上协议头占用的比例尽量的小。如此看来这2者在避免发送小包上是1致的,在用户控制的层面上,Nagle算法完全不受用户socket的控制,你只能简单的设置TCP_NODELAY而禁用它,CORK算法一样也是通过设置或清除TCP_CORK使能或禁用之,但是Nagle算法关心的是网络堵塞问题,只要所有的ACK回来则发包,而CORK算法却可以关心内容,在前后数据包发送间隔很短的条件下(很重要,否则内核会帮你将分散的包发出),即便你是分散发送多个小数据包,你也能够通过使能CORK算法将这些内容拼接在1个包内,如果此时用Nagle算法的话,则可能做不到这1点

Nagle算法的门坎

实际上Nagle算法其实不是很复杂,他的主要职责是数据的积累,实际上有3个门坎:
1)缓冲区中的字节数到达了1定量;
2)等待了1定的时间(1般的Nagle算法都是等待200ms);
3)紧急数据发送。
这3个门坎的任何1个到达都必须发送数据了。1般情况下,如果数据流量很大,第2个条件是永久不会起作用的,但当发送小的数据包时,第2个门坎就发挥作用了,避免数据被无穷的缓存在缓冲区不是好事情哦

  1. 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 算法是指发送方发送的数据不会立即发出,而是先放在缓冲区, 等缓存区满了再发出. 发送完1批数据后, 会等待接收方对这批数据的回应,然后再发送下1批数据。
Nagle 算法适用于发送方需要发送大批量数据, 并且接收方会及时作出回应的场合, 这类算法通过减少传输数据的次数来提高通讯效力。
如果发送方延续地发送小批量的数据, 并且接收方不1定会立即发送响应数据, 那末Nagle算法会使发送方运行很慢。
3.2 TCP_CORK选项

TCP链接的进程中,默许开启Nagle算法,进行小包发送的优化。优化网络传输,统筹网络延时和网络堵塞。这个时候可以置位TCP_NODELAY关闭Nagle算法,有数据包的话直接发送保证网络时效性。
在进行大量数据发送的时候可以置位TCP_CORK关闭Nagle算法保证网络利用性。尽量的进行数据的组包,以最大mtu传输,如果发送的数据包大小太小则如果在0.6~0.8S范围内都没能组装成1个MTU时,直接发送。如果发送的数据包大小足够间隔在0.45内时,每次组装1个MTU进行发送。如果间隔大于0.4~0.8S则,每过来1个数据包就直接发送。
Nagle组织包的长度是由系统决定的,有时候我们知道我们会每一个1分钟产生1字节,共1000字节。如果完全由Nagle算法来发送的话,可能还是会1字节1字节发送[这是1种极端情况,假定返回ACK时间不是很长]。这个时候首先设置TCP_CORK能够阻塞住TCP[尽可能阻塞住],等我们write完1000字节以后,取消TCP_CORK,这个时候就可以够将1000字节1次发出。
总结:

TCP_CORK选项与TCP_NODELAY1样,是控制Nagle化的。
1)打开TCP_NODELAY选项,则意味着不管数据包是多么的小,都立即发送(不斟酌堵塞窗口)。
2)如果将TCP连接比喻为1个管道,那TCP_CORK选项的作用就像1个塞子。
设置TCP_CORK选项,就是用塞子塞住管道,而取消TCP_CORK选项,就是将塞子拔掉。
当TCP_CORK选项被设置时,TCP链接不会发送任何的小包,即只有当数据量到达MSS时,才会被发送。
1般当数据传输完成时,通常需要取消该选项,以防被塞住,这样才可让不够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算法的基本定义是任意时刻,最多只能有1个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指1个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。

    Nagle算法的规则(可参考tcp_output.c文件里tcp_nagle_check函数注释):

  (1)如果包长度到达MSS,则允许发送;

  (2)如果该包括有FIN,则允许发送;

  (3)设置了TCP_NODELAY选项,则允许发送;

  (4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
  (5)上述条件都未满足,但产生了超时(1般为200ms),则立即发送。


    Nagle算法只允许1个未被ACK的包存在于网络,它其实不管包的大小,因此它事实上就是1个扩大的停-等协议,只不过它是基于包停-等的,而不是基于字节停-等的。Nagle算法完全由TCP协议的ACK机制决定,这会带来1些问题,比如如果对端ACK回复很快的话,Nagle事实上不会拼接太多的数据包,虽然避免了网络堵塞,网络整体的利用率仍然很低。

    Nagle算法是silly window syndrome(SWS)预防算法的1个半集。SWS算法预防发送少许的数据,Nagle算法是其在发送方的实现,而接收方要做的时不要通告缓冲空间的很小增长,不通知小窗口,除非缓冲区空间有显著的增长。这里显著的增长定义为完全大小的段(MSS)或增长到大于最大窗口的1半。

注意:BSD的实现是允许在空闲链接上发送大的写操作剩下的最后的小段,也就是说,当超过1个MSS数据发送时,内核先顺次发送完n个MSS的数据包,然后再发送尾部的小数据包,其间不再延时等待。(假定网络不阻塞且接收窗口足够大)

    举个例子,比如之前的blog中的实验,1开始client端调用socket的write操作将1个int型数据(称为A块)写入到网络中,由于此时连接是空闲的(也就是说还没有未被确认的小段),因此这个int型数据会被马上发送到server端,接着,client端又调用write操作写入‘\r\n’(简称B块),这个时候,A块的ACK没有返回,所以可以认为已存在了1个未被确认的小段,所以B块没有立即被发送,1直等待A块的ACK收到(大概40ms以后),B块才被发送。全部进程如图所示:

Nagle 算法 - Shiney - Shiney
这里还隐藏了1个问题,就是A块数据的ACK为何40ms以后才收到?这是由于TCP/IP中不单单有nagle算法,还有1个TCP确认延迟机制 。当Server端收到数据以后,它其实不会马上向client端发送ACK,而是会将ACK的发送延迟1段时间(假定为t),它希望在t时间内server端会向client端发送应对数据,这样ACK就可以够和应对数据1起发送,就像是应对数据捎带着ACK过去。在我之前的时间中,t大概就是40ms。这就解释了为何’\r\n’(B块)总是在A块以后40ms才发出。
固然,TCP确认延迟40ms其实不是1直不变的,TCP连接的延迟确认时间1般初始化为最小值40ms,随后根据连接的重传超时时间(RTO)、上次收到数据包与本次接收数据包的时间间隔等参数进行不断调剂。另外可以通过设置TCP_QUICKACK选项来取消确认延迟。
关于TCP确认延迟的详细介绍可参考:http://blog.csdn.net/turkeyzhou/article/details/6764389

  1. TCP_NODELAY 选项

    默许情况下,发送数据采取Negale 算法。这样虽然提高了网络吞吐量,但是实时性却下降了,在1些交互性很强的利用程序来讲是不允许的,使用TCP_NODELAY选项可以制止Negale 算法。
    
    此时,利用程序向内核递交的每一个数据包都会立即发送出去。需要注意的是,虽然制止了Negale 算法,但网络的传输依然遭到TCP确认延迟机制的影响。
    
  2. TCP_CORK 选项

    所谓的CORK就是塞子的意思,形象地理解就是用CORK将连接塞住,使得数据先不发出去,等到拔去塞子后再发出去。设置该选项后,内核会尽力把小数据包拼接成1个大的数据包(1个MTU)再发送出去,固然若1定时间后(1般为200ms,该值尚待确认),内核依然没有组合成1个MTU时也必须发送现有的数据(不可能让数据1直等待吧)。
    但是,TCP_CORK的实现可能其实不像你想象的那末完善,CORK其实不会将连接完全塞住。内核其实其实不知道利用层到底甚么时候会发送第2批数据用于和第1批数据拼接以到达MTU的大小,因此内核会给出1个时间限制,在该时间内没有拼接成1个大包(努力接近MTU)的话,内核就会无条件发送。也就是说若利用层程序发送小包数据的间隔不够短时,TCP_CORK就没有1点作用,反而失去了数据的实时性(每一个小包数据都会延时1定时间再发送)。
    
  3. Nagle算法与CORK算法区分

    Nagle算法和CORK算法非常类似,但是它们的着眼点不1样,Nagle算法主要避免网络由于太多的小包(协议头的比例非常之大)而堵塞,而CORK算法则是为了提高网络的利用率,使得整体上协议头占用的比例尽量的小。如此看来这2者在避免发送小包上是1致的,在用户控制的层面上,Nagle算法完全不受用户socket的控制,你只能简单的设置TCP_NODELAY而禁用它,CORK算法一样也是通过设置或清除TCP_CORK使能或禁用之,但是Nagle算法关心的是网络堵塞问题,只要所有的ACK回来则发包,而CORK算法却可以关心内容,在前后数据包发送间隔很短的条件下(很重要,否则内核会帮你将分散的包发出),即便你是分散发送多个小数据包,你也能够通过使能CORK算法将这些内容拼接在1个包内,如果此时用Nagle算法的话,则可能做不到这1点。
    
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生