TCP报文发送的那些事( 二 )


TCP头里有一个字段叫Window,又叫Advertised-Window,这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据 。于是发送端就可以根据这个接收端的处理能力来发送数据,否则会导致接收端处理不过来 。
我们可以将发送的数据分为以下四类,将它们放在时间轴上统一观察 。

TCP报文发送的那些事

文章插图
 
  • Sent and Acknowledged: 表示已经发送成功并已经被确认的数据,比如图中的前31个字节的数据
  • Send But Not Yet Acknowledged:表示发送但没有被确认的数据,数据被发送出去,没有收到接收端的ACK,认为并没有完成发送,这个属于窗口内的数据 。
  • Not Sent,Recipient Ready to Receive:表示需要尽快发送的数据,这部分数据已经被加载到缓存等待发送,也就是发送窗口中 。接收方ACK表示有足够空间来接受这些包,所以发送方需要尽快发送这些包 。
  • Not Sent,Recipient Not Ready to Receive:表示属于未发送,同时接收端也不允许发送的,因为这些数据已经超出了发送端所接收的范围
除了四种不同范畴的数据外,我们可以看到上边的示意图中还有三种窗口 。
•Window Already Sent:已经发送了,但是没有收到ACK,和Send But Not Yet Acknowledged部分重合 。
•Usable Window : 可用窗口,和Not Sent,Recipient Ready to Receive部分重合
•Send Window: 真正的窗口大小 。建立连接时接收方会告知发送方自己能够处理的发送窗口大小,同时在接收过程中也不断的通告能处理窗口的大小,来实时调节 。
下面,我们来看一下滑动窗口的滑动 。下图是滑动窗口滑动的示意图 。
TCP报文发送的那些事

文章插图
 
当发送方收到发送数据的确认消息时,会移动发送窗口 。比如上图中,接收到36字节的确认,将其之前的5个字节都移除发送窗口,然后46-51的字节发出,最后将52到56的字节加入到可用窗口 。
下面我们来看一下整体的示意图 。
TCP报文发送的那些事

文章插图
 
 图片来源为tcpipguide.
client端窗口中不同颜色的矩形块代表的含义和上边滑动窗口示意图中相同 。我们只简单看一下第二三四步 。接收端发送的TCP报文window为260,表示发送窗口减少100,可以发现黑色矩形缩短了,也就是发送窗口减少了100 。并且ack为141,所以发送端将140个字节的数据从发送窗口中移除,这些数据从Send But Not Yet Acknowledged变为Sent and Acknowledged,也就是从蓝色变成紫色 。然后发送端发送180字节的数据,就有180字节的数据从Not Sent,Recipient Ready to Receive变为Send But Not Yet Acknowledged,也就是从绿色变为蓝色 。
Nagle算法
上述滑动窗口会出现一种Silly Window Syndrome的问题,当接收端来不及取走Receive windows里的数据,会导致发送端的发送窗口越来越小 。到最后,如果接收端腾出几个字节并告诉发送端现在有几个字节的window,而我们的发送端会义无反顾地发送这几个字节 。
只为了发送几个字节,要加上TCP和IP头的40多个字节 。这样,效率太低,就像你搬运物品,明明一次可以全部搬完,但是却偏偏一次只搬一个物品,来回搬多次 。
为此,TCP引入了Nagle算法 。应用进程调用发送方法时,可能每次只发送小块数据,造成这台机器发送了许多小的TCP报文 。对于整个网络的执行效率来说,小的TCP报文会增加网络拥塞的可能 。因此,如果有可能,应该将相临的TCP报文合并成一个较大的TCP报文(当然还是小于MSS的)发送 。
Nagle算法的规则如下所示(可参考tcp_output.c文件里tcp_nagle_check函数注释):
•如果包长度达到MSS,则允许发送;
•如果该包含有FIN,则允许发送;
•设置了TCP_NODELAY选项,则允许发送;
•未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
•上述条件都未满足,但发生了超时(一般为200ms),则立即发送 。
当对请求的时延非常在意且网络环境非常好的时候(例如同一个机房内),Nagle算法可以关闭 。使用TCP_NODELAY套接字选项就可以关闭Nagle算法




推荐阅读