TCP的坚持定时器章节目录
1.一个例子
2.糊涂窗口综合症
前言
可以在图20-3中看到这种情况。当发送方接收到报文段9时,它打开被报文段8关闭的窗口并立即开始发送数据。TCP必须能够处理打开此窗口的ACK(报文段9)丢失的情况。ACK的传输并不可靠,也就是说,TCP不对ACK报文段进行确认,TCP只确认那些包含有数据的ACK报文段。
如果一个确认丢失了,则双方就有可能因为等待对方而使连接终止:接收方等待接收数据(因为它已经向发送方通告了一个非0的窗口),而发送方在等待允许它继续发送数据的窗口更新。为防止这种死锁情况的发生,发送方使用一个坚持定时器(persisttimer)来周期性地向接收方查询,以便发现窗口是否已增大。这些从发送方发出的报文段称为窗口探查(window probe)。
一个例子
为什么这些间隔总是比5、6、12、24、48和60小一个零点几秒呢?因为这些探查被TCP的500ms定时器超时例程所触发。当定时器时间到时,就发送窗口探查,并大约在4ms之后收到一个应答。接收到应答使得定时器被重新启动,但到下一个时钟滴答之间的时间则约为500减4ms。
计算坚持定时器时使用了普通的TCP指数退避。对一个典型的局域网连接,首次超时时间算出来是1.5秒,第2次的超时值增加一倍,为3秒,再下次乘以4为6秒,之后再乘以8为12秒等。但是坚持定时器总是在5~60秒之间,这与我们在图22-1中观察到的现象一致。窗口探查包含一个字节的数据(序号为9217)。TCP总是允许在关闭连接前发送一个字节的数据。请注意,尽管如此,所返回的窗口为0的ACK并不是确认该字节(它们确认了包括9216在内的所有数据),因此这个字节被持续重传。
坚持状态与第21章中介绍的重传超时之间一个不同的特点就是TCP从不放弃发送窗口探查。这些探查每隔60秒发送一次,这个过程将持续到或者窗口被打开,或者应用进程使用的连接被终止。
糊涂窗口综合症
在连接的一方需要发送数据但对方已通告窗口大小为0时,就需要设置TCP的坚持定时器。发送方使用与第21章类似的重传间隔时间,不断地探查已关闭的窗口。这个探查过程将一直持续下去。
当运行一个例子来观察坚持定时器时,我们还观察到了TCP的避免出现糊涂窗口综合症的现象。这就是使TCP避免通告小的窗口大小或发送小的报文段。在我们的例子中,可以观察到发送方和接收方为避免糊涂窗口综合症所使用的策略。
当接收到来自发送方的数据时,接收方缓存中的数据增加,而当应用进程从缓存中读取数据时,数据就减少。接下来我们关注的是接收方发给发送方的窗口通告以及这些窗口通告是什么。这样就可以使我们看到接收方是如何避免糊涂窗口综合症的。
前4个数据报文段及其ACK(报文段1~5)表示发送方正在填充接收方的缓存。在那个时刻发送方停止了发送,但仍然有更多的数据需要发送。它将自己的坚持定时器置为最小值5分钟。当坚持定时器时间到时,就发送出1个字节的数据(报文段6)。接收的应用进程已经从接收缓存中读取了256字节的数据(在时刻3.99),因此这个字节被接受并被确认(报文段7段)。但是通告窗口仍为0,由于接收方仍然没有足够的空间来接收一个满长度的报文,或者不能腾出缓存空间的一半。这就是接收方的糊涂窗口避免措施。
发送方的坚持定时器被复位,并在5秒后再次到时(在时刻10.151)。然后又发送一个字节并被确认(报文段8和9),而接收方的缓存空间还不够用(1022字节),使得通告窗口为0。
发送方的坚持定时器在时刻15.151再次时间到,又发送了另一个字节并被确认(报文段10和11)。这一次由于接收方有1533字节的有效缓存空间,因此通告了一个非0窗口。发送方立即利用这个窗口发送了1024字节的数据(报文段12)。对这1024字节数据的确认(报文段13)通告其窗口为509字节。这看起来与我们在前面看到的小窗口通告相抵触。
在这里之所以发生这种情况,是因为报文段11段通告了一个大小为1533字节的窗口,而发送方只使用了其中的1024字节。如果在报文段13中的ACK通告其窗口为0,就会违反窗口的右边沿不能向左边沿移动而导致窗口收缩的TCP原则(见第20.3节)。这就是为什么必须通告一个509字节的窗口的原因。
接下来我们看到发送方没有立即向这个小窗口发送数据。这就是发送方采取的糊涂窗口避免策略。相反,它等待另一个坚持定时器在时刻20.151到时间,并在该时刻发送509字节的数据。尽管它最终还是发送了一个长度为509字节的小数据段,但在发送前它等待了5秒钟,看是否会有一个ACK到达,以便可以将窗口开得更大。这509字节的数据使得接收缓存仅剩下768字节的有效空间,因此接收方通告窗口为0(报文段15)。
坚持定时器在时刻25.151再次到时间,发送方发送1个字节,于是接收缓存中有1279字节的可用空间,这就是在报文段17所通告的窗口大小。
发送方只有另外的511个字节的数据需要发送,因此在收到1279的窗口通告后立刻发送了这些数据(报文段18)。这个报文段也带有FIN标志。接收方确认数据和FIN,并通告窗口大小为767。
由于发送应用进程在执行完6个1024字节的写操作后发出关闭命令,发送方的连接从ESTABLISHED状态转变到FIN_WAIT_1状态,再到FIN_WAIT_2状态(见图18-12)。它一直处于这个状态,直到收到对方的FIN。在这个状态上没有设置定时器(回忆我们在18.6节结束时的讨论),因为它在报文段18中发送的FIN被报文段19确认。这就是为什么我们看到发送方直到接收到FIN(报文段21)为止没有发送其他任何数据的原因。
接收应用进程继续每隔2秒从接收缓存区中读取256个字节的数据。为什么在时刻39.99发送ACK(报文段20)呢?这是因为应用进程在时刻39.99读取数据时,接收缓存中的可用空间已经从原来通告的767(报文段19)变为2816,这相当于接收缓存中增加了额外的2049字节的空间。回忆本节开始讲的第1个规则,因为现在接收缓存已经增加了其空间的一半,因此接收方现在发送窗口更新。这意味着每次当应用进程从TCP的接收缓存中读取数据时,接收的TCP将检查是否需要更新发送窗口。
应用进程在时间51.99发出最后一个读操作,然后收到一个文件结束标志,因为缓存已经变空。这就导致了最后两个完成连接终止的报文段(报文段21和22)的发送。