引言
我们已经看到TCP通过让接收方指明希望从发送方接收的数据字节数(即窗口大小)来进行流量控制。如果窗口大小为0,这将阻止发送方发送数据,直到窗口变为非0为止。
可以在这一节图20-3中看到这种情况。当发送方接收到报文段9时,它打开被报文段8关闭的窗口并立即开始发送数据。TCP必须能够处理打开此窗口的ACK(报文段9)丢失的情况。如果一个确认丢失了,则双方就有可能因为等待对方而使连接终止:接收方等待接收数据(因为它已经向发送方通告了一个非0的窗口),而发送方在等待允许它继续发送数据的窗口更新。为防止这种死锁情况的发生,发送方使用一个持续定时器(persist timer)来周期性地向接收方查询窗口是否已增大。这些从发送方发出的报文段称为窗口探测(window probe)报文。一个例子
在这个例子中,我们先在主机svr4上启动一个服务器进程,这个进程在读取数据之前我们让它睡眠一段时间。然后在主机bsdi上启动客户进程,发送9216字节数据到svr4上的服务器进程,填满它的接收缓冲区。tcpdump输出如下图所示:
观察蓝色框线内(书中作者未解释为什么前两个超时时间为5秒,我实际实验并未看到这两个5秒的超时,所以我们这里可以忽略它)的持续定时器超时时间,它们分别是6、12、24、48、60、60、60。这说明持续定时器使用指数退避方法计算下一次的超时值,而且最大超时时间为60秒。
持续定时器与上一节介绍的重传超时之间一个不同之处就是:TCP从不放弃发送窗口探测报文。这些窗口探测报文每隔60秒发送一次,这个过程将持续到或者窗口被打开,或者连接被终止。
糊涂窗口综合症
基于窗口的流量控制方案,如TCP所使用的,会导致一种被称为“糊涂窗口综合症(Silly Window Syndrome,SWS)”的状况。如果发生这种情况,则少量的数据将通过连接进行交换,而不是满长度的报文段。
该现象可发生在两端中的任何一端:接收方可以通告一个小的窗口(而不是一直等到有大的窗口时才通告),而发送方也可以发送少量的数据(而不是等待其他的数据以便发送一个大的报文段)。可以在任何一端采取措施避免出现糊涂窗口综合症的现象。
1) 接收方不通告小窗口。通常的算法是接收方不通告一个比当前窗口大的窗口(可以为0),除非窗口可以增加一个报文段大小(双方协商的最大报文段大小MSS)或者可以增加接收方缓冲区大小的一半,不论实际有多少。
2) 发送方避免出现糊涂窗口综合症的措施是只有以下条件之一满足时才发送数据:(a)可以发送一个满长度的报文段;(b)可以发送至少是接收方通告窗口大小一半的报文段;(c)可以发送任何数据并且不希望接收ACK(也就是说,我们没有还未被确认的数据)或者该连接上禁用了Nagle算法。
一个例子
这个例子中,我们从sun主机往bsdi主机的服务器进程发送6个1024字节的数据。服务器进程先睡眠一段时间使得它的接收缓冲区被填满,然后在每次读最大256字节数据前先休眠2秒钟。tcpdump输出的时间序列如下:
为了分析接收方发送的10个窗口大小,我们还需要知道接收方各个时刻接收缓冲区内可用空间大小,这些信息如下图所示:
在分析之前我们还要知道,在本例中,双方协商的最大报文段大小MSS为1024字节,接收缓冲区的大小是4096字节。接下来我们一个个分析窗口大小值。
1. 接收缓冲区可用空间为0,于是通告窗口大小为0。
2. 接收缓冲区可用空间为255,小于MSS(1024),也小于接收缓冲区大小的一半(2048),于是通告窗口大小为0。
3. 接收缓冲区可用空间为1022,同2。
4. 接收缓冲区可用空间为1533,大于MSS(1024),于是通告窗口大小为1533。
5.接收缓冲区可用空间为509,小于一个报文段大小。但是,上一个报文段通告的窗口大小为1533,而发送方只使用了其中的1024字节。如果此时通告窗口为0,就会违反窗口的右边沿不能向左边沿移动而导致窗口收缩的原则。于是通告窗口大小为509。
6.接收缓冲区可用空间为768,同2。
7.接收缓冲区可用空间为1279,大于MSS(1024),于是通告窗口大小为1279。
8.接收缓冲区可用空间为768,同5,但为什么发出的通告窗口大小为767?因为FIN标志占用了1个序号。
9. 接收缓冲区可用空间为2816,2816 - 767(上次通告的窗口大小) = 2049,大于接收缓冲区的一半(2048),于是通告窗口大小为2816。这意味着每次当应用进程从TCP的接收缓冲中读取数据时,TCP将检查是否需要更新发送窗口。
10.接收缓冲区可用空间为4096,4096 - 2816(上次通告的窗口大小) = 1280,大于MSS(1024),于是通告窗口大小为4096。