文章目录
- 延迟确认
- Nagle算法
- Nagle算法遇上延迟确认
- 关闭Nagle算法
一些有关TCP通信量的研究如[Caceresetal.1991]发现,如果按照分组数量计算,约有一半的TCP报文段包含成块数据(如 FTP、电子邮件和 Usenet新闻),另一半则包含交互数据(如Telnet和Rlogin)。如果按字节计算,则成块数据与交互数据的比例约为 90 %和10 %。这是因为成块数据的报文段基本上都是满长度的,而交互数据则小得多。
为了同时处理上述两类数据,
延迟确认
对于键入一个交互命令时所产生的数据流,通常情况下每一个交互按键都会产生一个数据分组,也就是说,每次从客户传到服务器的是一个字节的按键。这样对每一个按键都会产生4个报文段:(1)来自客户的交互按键;(2)来自服务器的按键确认;(3)来自服务器的按键回显;( 4)来自客户的按键回显确认。
通过延迟确认的技术,可以将按键确认与按键回显合并发送,以减少网络的负载。
使用延迟确认时,通常TCP在接收到数据时并不立即发送ACK;相反,它推迟发送,以便将ACK与需要沿该方向发送的数据一起发送(有时称这种现象为数据捎带 ACK)。绝大多数实现采用的时延为 200 ms,也就是说,TCP将以最大200 ms的时延等待是否有数据一起发送。
Nagle算法
Nagle算法是一种解决小包问题的方法,这种问题指的是应用程序一次产生一字节数据,这样会导致网络由于太多的包而过载。例如,如果应用程序一次发送1字节,那么一次实际上会有20字节的IP首部+20字节的TCP首部+1字节的数据被传输,这些小分组会增加出现拥塞的可能。
该算法要求一个 TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组。 TCP会收集这些少量的小分组,并在确认到来时以一个分组的方式发出去。该算法的优越之处在于它是自适应的:确认到达得越快,数据也就发送得越快。而在希望减少微小分组数目的低速广域网上,则会发送更少的分组。
Nagle算法的规则如下:
- 如果包长度达到MSS,则允许发送;
- 如果该包含有FIN,则允许发送;
- 设置了TCP_NODELAY选项,则允许发送;
- 未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
- 上述条件都未满足,但发生了超时(一般为200ms),则立即发送。
举个例子,这其实就像拼车,几十个人一起旅游,要是一人一辆车,那就得好几十辆车,开到哪哪要堵,但要是叫个大巴,一两辆就足够了,也不妨碍交通。
Nagle算法遇上延迟确认
为什么会变成这样呢……第一次,减少了网络负载;第一次解决了小包问题。这两件愉快的事情交织在了一起。而这两份喜悦,又会给我带来许许多多的喜悦。我本应该获得了这种如梦一般的幸福时光才对。可是,为什么,会变成现在这样呢……
Nagle算法与延迟确认机制有共存的情况下就会有一些非常糟糕的状况,如果进行write-write-read操作,也就是向对端连续发送两次小片数据时,就会陷入超时等待。比如举一个场景:1P和2P进行通信,1P使用Nagle算法,2P有延迟确认机制。
-
首先1P发送一个数据包给2P,2P因为延迟确认机制先不回复,等待新数据过来再发送捎带ACK
-
1P第二次发送数据小于MSS,因为Nagle算法,要等待收到2P对之前发送数据的确认才发送当前数据。
-
这时候就出现了这样的情况:1P等待2P的确认,2P等待1P发送数据。
从上面的描述看,显然已经死锁了,因为有超时机制,所以死锁最终会解除,但是这却会浪费一个等待超时的时间。
关闭Nagle算法
默认情况下Nagle算法是开启的,以降低传输包的数量。但有时我们也需要关闭 Nagle算法:
- 对端不会向本端发送数据,或是对时延比较敏感的操作,如一些用户的交互命令,这时候就无法捎带ACK
- 如上,Nagle算法和延迟确认相遇且有write-write-read操作的时候,但是对于这种情况,推荐优先使用其他方法:
(1)使用writev,而不是两次调用write,单个writev调用会使tcp输出一次而不是两次,只产生一个tcp分节,这是首选方法;
(2)把两次写操作的数据复制到单个缓冲区,然后对缓冲区调用一次write;
因为关闭Nagle算法对网络不太好,通常不考虑
接口API提供了TCP_NODELAY选项来关闭Nagle算法。