TCP_NODELAY和TCP_CORK nagle算法和cork算法

TCP_NODELAY

默认情况下,发送数据採用Nagle 算法。这样尽管提高了网络吞吐量,可是实时性却减少了,在一些交互性非常强的应用程序来说是不同意的。使用TCP_NODELAY选项能够禁止Nagle 算法。

此时,应用程序向内核递交的每一个数据包都会马上发送出去。须要注意的是,尽管禁止了Nagle 算法,但网络的传输仍然受到TCP确认延迟机制的影响。

TCP_CORK

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

内核事实上并不知道应用层究竟什么时候会发送第二批数据用于和第一批数据拼接以达到MTU的大小,因此内核会给出一个时间限制。在该时间内没有拼接成一个大包(努力接近MTU)的话。内核就会无条件发送。

也就是说若应用层程序发送小包数据的间隔不够短时。TCP_CORK就没有一点作用,反而失去了数据的实时性(每一个小包数据都会延时一定时间再发送)。


Nagle算法

TCP/IP协议中,不管发送多少数据。总是要在数据前面加上协议头。同一时候,对方接收到数据。也须要发送ACK表示确认。

为了尽可能的利用网络带宽。TCP总是希望尽可能的发送足够大的数据。(一个连接会设置MSS參数。因此,TCP/IP希望每次都可以以MSS尺寸的数据块来发送数据)。Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着很多小数据块。
  Nagle算法的基本定义是随意时刻,最多仅仅能有一个未被确认的小段

所谓“小段”。指的是小于MSS尺寸的数据块,所谓“未被确认”。是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。
  Nagle算法的规则(可參考tcp_output.c文件中tcp_nagle_check函数凝视):

(1)假设包长度达到MSS,则同意发送。
(2)假设该包括有FIN。则同意发送;
(3)设置了TCP_NODELAY选项,则同意发送。
(4)未设置TCP_CORK选项时。若全部发出去的小数据包(包长度小于MSS)均被确认。则同意发送。
(5)上述条件都未满足,但发生了超时(一般为200ms),则马上发送。

  Nagle算法仅仅同意一个未被ACK的包存在于网络,它并无论包的大小。因此它其实就是一个扩展的停-等协议,仅仅只是它是基于包停-等的,而不是基于字节停-等的。Nagle算法全然由TCP协议的ACK机制决定。这会带来一些问题。比方假设对端ACK回复非常快的话。Nagle其实不会拼接太多的数据包。尽管避免了网络拥塞。网络整体的利用率依旧非常低。

Nagle算法是silly window syndrome(SWS)预防算法的一个半集。SWS算法预防发送少量的数据,Nagle算法是其在发送方的实现。而接收方要做的是不要通告缓冲空间的非常小增长,不通知小窗体。除非缓冲区空间有显著的增长。这里显著的增长定义为全然大小的段(MSS)或增长到大于最大窗体的一半。
  注意:BSD的实现是同意在空暇链接上发送大的写操作剩下的最后的小段,也就是说,当超过1个MSS数据发送时。内核先依次发送完n个MSS的数据包,然后再发送尾部的小数据包。其间不再延时等待。(如果网络不堵塞且接收窗体足够大)
举个样例。比方之前的blog中的实验。一開始client端调用socket的write操作将一个int型数据(称为A块)写入到网络中。因为此时连接是空暇的(也就是说还没有未被确认的小段)。因此这个int型数据会被立即发送到server端,接着,client端又调用write操作写入‘ ’(简称B块),这个时候,A块的ACK没有返回,所以能够觉得已经存在了一个未被确认的小段。所以B块没有立即被发送。一直等待A块的ACK收到(大概40ms之后),B块才被发送。

整个过程如图所看到的:

这里还隐藏了一个问题,就是A块数据的ACK为什么40ms之后才收到?这是由于TCP/IP中不唯独nagle算法,另一个TCP确认延迟机制 。当Server端收到数据之后,它并不会立即向client端发送ACK。而是会将ACK的发送延迟一段时间(如果为t),它希望在t时间内server端会向client端发送应答数据,这样ACK就行和应答数据一起发送,就像是应答数据捎带着ACK过去。在我之前的时间中。t大概就是40ms。

这就解释了为什么' '(B块)总是在A块之后40ms才发出。
  当然,TCP确认延迟40ms并非一直不变的,TCP连接的延迟确认时间一般初始化为最小值40ms,随后依据连接的重传超时时间(RTO)、上次收到数据包与本次接收数据包的时间间隔等參数进行不断调整。

另外能够通过设置TCP_QUICKACK选项来取消确认延迟。


CORK算法

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



在JAVA编程中,能够通过Socket中的

setTcpNoDelay

public void setTcpNoDelay(boolean on)
                   throws SocketException
启用/禁用 TCP_NODELAY(启用/禁用 Nagle 算法)。

參数:
on - 为 true 表示启用 TCP_NODELAY;为 false 表示禁用。
抛出:
SocketException - 假设底层协议出现错误,比如 TCP 错误。

从下面版本号開始:
JDK1.1
另请參见:
getTcpNoDelay()

来设置是否启用tcp_nodelay,提高网络吞吐量的同一时候必定减少了网络的实时性。












原文地址:https://www.cnblogs.com/mfmdaoyou/p/7160098.html