c++应用网络编程之十五Nagle算法
一、TCP通信的特定场景
在进行网络通信编程时,开发者都知道,自己发送或接收的数据,其实在网络中传输时,还带有协议不同层间的包的元数据,而这些元数据,也是要占用发送带宽的。这时候就会有一个问题,如果发送的数据很少,少到比元数据都少时,会出现什么情况?假如两个人聊天,互相都只发一个“嗯”,“啊”,甚至只有一个字母,不就是这种情况么。如果从实际的发送来看,发送一个字节有效的数据,会产生20个字节的IP和20个TCP的元数据(包头)即使用了40倍的数据量发送了一个字节。这些小包的数据不但增加了发送的浪费还有可能引起网络的拥塞。
如果实际情况紧急,需要这样,那么本也无可厚非。但可能大多数的情况都是类似上面假设的场景中的不紧急的情况,那么在计算机体系中,老扣儿的性格就得发挥出来,这时候就出现了一种方法或者说技术,把这些有效数据等等,看看能不能再收到一些有效数据,组成一个包发送,这样增加有效载荷。
二、Nagle算法
而题目中提到的Nagle算法就是针对发送小包来解决问题的一种算法,它主要用在发送端。在标准文档中有对其的伪代码的描述:
if there is new data to send
if the window size >= MSS and available data is >= MSS
send complete MSS segment now
else
if there is unconfirmed data still in the pipe
enqueue data in the buffer until an acknowledge is received
else
send data immediately
end if
end if
end if
说明:MSS,Maximum Segment Size,最大报文段长度
简单说明一下Nagle算法的应用条件:
1、第一包,发送
2、如果包长度到达MSS,则发送
3、如果包含有FIN,则发送
4、设置了TCP_NODELAY,发送
5、未设置TCP_CORK,已接收之前所有数据包ACK,则发送
6、未满足上述条件,超时(一般200ms),发送
Nagle算法只是试图减少网络中的小包发送数量,而不是严格禁止,这一点一定要明白。
Nagle算法在Delay ACK时,会有一些副作用,导致网络通信效率的降低,正如前面分析,小包+延迟ACK,有可能形成通信的死锁,而要想解除这种死锁就需要等待延时的到期,大约需要40~500ms不等。所以经引用开发者们的注意。解决的方法也有两种,一种是禁止使用Nagle算法(下面会说明如何禁止),另外一种是RFC中提供的解决方案,即将传输方式修改,即从写写读,改成写读写读或写写写都可以。
三、Cork算法
在内核中还有一个Cork算法,默认是关闭的,它的功能和Nagle算法有些类似。在Cork算法中,要求在任何情况下,小包都必须超时发出而不是利用接收到反馈的ACK做为条件来发送。在实际的开发中可以使用下面的方式将其开启:
const char opt=1;
setsockopt(fd, SOL_TCP, TCP_CORK, &opt, sizeof(char));
有兴趣的可以看一看内核的源码是如何处理的,源码之前,了无秘密。
四、Nagle算法的应用
Nagle算法就是针对小包优化的,所以如果实际的开发场景中有大量的小包数据,则可以使用这个算法,而Nagle算法默认是开启的。但在实际的应用环境中,如何确定这个算法是开启的呢?可以使用下面几个方式:
1、使用系统中的配置查看命令或查看相关文件,如在Windows上可以使用netsh命令,在Linux上可查看相应的配置文件
2、使用相关的网络包分析工具,如Wireshark等,进行抓包,如果有大量的小包被发送,则可能禁止了此算法
3、如果有源码可以查看源码有无相关的设置
4、其它(如自身的相关经验)
那么,如何在某些情况下禁止Nagle算法呢?可以使用下面的命令
const char opt=1;
setsockopt(fd, SOL_TCP, TCP_NODELAY,&opt, sizeof(char));
需要说明的是,这么设置只是禁用了Nagle算法,但是没有禁用Delay ACK。
五、总结
正如前面反复分析阐述的,任何技术都其产生的时代背景和当时的意义,而Nagle算法也是如此。其在通信延时较低的场景下几乎没有什么意义。因为其靠ACK返回来进行判定传输发送的一个前提,所以比如在局域网中,回复非常快,其实就没有办法触发此算法了。而且在有些场合下需要快速的传输,也会禁止此算法。所以开发者一定要把算法的应用场景和技术内涵搞清楚,才能对相关的算法进行准确的应用,而不是自己想怎么样就怎么样。
实践出真知!