第21章TCP的超时与重传

21.8拥塞举例(续)

通过使用tcmdump和插口排错选项(在第21.4节进行了介绍)来观察一个连接,就会在发送每一个报文段时看到cwnd和ssthresh的值。如果MSS为256字节,则cwnd和ssthresh的初始值分别为256和65535字节。每当收到一个ACK时,我们可以看到cwnd增加了一个MSS,取值分别为512,768,1024,1280等。假定不会发生拥塞,则最终拥塞窗口将超过接收方的通告窗口,意味着通告窗口将对数据流进行限制。

一个更有趣的例子是观察在拥塞发生时的情况。使用与21.4节同样的例子。当这个例子运行时发生了4次拥塞。为建立连接而发送的初始SYN有一个因超时而引起的重传(见图21-5),接着在数据传输过程中有3个分组丢失(见图21-6)。

图21-9显示了当初始SYN重传并接着发送了前7个数据报文段时变量cwnd和ssthresh的值(在图21-2中显示了最初的数据报文段及其ACK之间的交换过程)。使用tcpdump的记号来表示数据字节:1:257(256)表示第1~256字节。

当SYN的超时发生时,ssthresh被置为其最小取值(512字节,在本例中表示2个报文段)。为进入慢启动阶段,cwnd被置为1个报文段(256字节,与当前值一致)。

当收到SYN和ACK时,没有对这两个变量做任何修改,因为新的数据还没有被确认。当ACK257到达时,因为cwnd小于等于ssthresh,因此仍然处于慢启动阶段,于是将cwnd增加256字节。当收到ACK513时,进行同样的处理。

当ACK769到达时,我们不再处于慢启动状态,而是进入了拥塞避免状态。新的cwnd值按以下方法计算:
速读原著-TCP/IP(拥塞举例)_慢启动
考虑到cwnd实际上以字节而非以报文段来维护,因此这就是我们前面提到的增加1/cwnd。在这个例子中我们计算
速读原著-TCP/IP(拥塞举例)_数据_02
为885字节(使用整数算法)。当下一个ACK1025到达时,我们计算
速读原著-TCP/IP(拥塞举例)_取值_03
为991字节(在这些表达式中包括了不正确的256/8项来匹配实现计算的数值,正如我们在前面标注的那样)。
速读原著-TCP/IP(拥塞举例)_数据_04
这个cwnd持续增加一直到在图21-6所示的发生在10秒左右的第1次重传。图21-10是使用与图21-6相同数据得到的图表,并给出了cwnd增加的数值。

本图中cwnd的前6个值就是我们为图21-9所计算的数值。在这个图中,要想直观分辨出在慢启动过程中的指数增加和在拥塞免过程中的线性增加之间的区别是不可能的,因为慢启动的过程太快。

我们需要解释在重传的3个点上所发生的情况。回想起每个重传都是因为收到3个重复的ACK,表明1个分组丢失了。这就是21.7节的快速重传算法。ssthresh立即设置为当重传发生时正在起作用的窗口大小的一半,但是在接收到重复ACK的过程中cwnd允许保持增加,这是因为每个重复的ACK表示1个报文段已离开了网络(接收TCP已缓存了这个报文段,等待所缺数据的到达)。这就是快速恢复算法。
与图20-9类似,图21-10表示了cwnd和ssthresh的数值。第一列上的报文段编号与图21-7对应。
速读原著-TCP/IP(拥塞举例)_取值_05
速读原著-TCP/IP(拥塞举例)_重传_06
cwnd的值一直持续增加,从图21-9中对应于报文段12的最终取值(1089)到图21-11中对应于报文段58的第一个取值(2426),而ssthresh的值则保持不变(512),这是因为在此过程中没有出现过重传。
当最初的2个重复的ACK(报文段60和61)到达时它们被计数,而cwnd保持不变(也就是图21-10中处理重传之前的平坦的一段)。然而,当第3个重复的ACK到达时,ssthresh被置为cwnd的一半(四舍五入到报文段大小的下一个倍数),而cwnd被置为ssthresh加上所收到的重复的ACK数乘以报文段大小(也即1024加上3倍的256),然后发送重传数据。

又有5个重复的ACK到达(报文段64~66,68和70),每次cwnd增加1个报文段长度。最后一个新的ACK(报文段72段)到达时,cwnd被置为ssthresh(1024)并进入正常的拥塞避免过程。由于cwnd小于等于ssthresh(现在相等),因此报文段的大小增加到cwnd,取值为1280。当下一个新的ACK到达(没有在图21-11中表示出来)时,cwnd大于ssthresh,取值为1363。在快速重传和快速恢复阶段,我们收到报文段66、68和70中的重复的ACK后才发送新的数据,而不是在接收到报文段64和65中重复的ACK之后就发送。这是cwnd的取值与未被确认的数据大小比较的结果。当报文段65到达时,cwnd为2048,但未被确认的数据有2304字节(9个报文段:46,48,50,52,54,55,57,59和63),因此不能发送任何数据。当报文段65到达后,cwnd被置为2304,此时我们仍不能进行发送。但是当报文段66到达时,cwnd为2560,所以我们可以发送1个新的数据报文段。类似地,当报文段68到达时,cwnd等于2816,该数值大于未被确认的2560字节的数据大小,因此我们可以发送另1个新的数据报文段。报文段70到达时也进行了类似的处理。

在图21-10中的时刻14.3发生下一个重传,也是因为收到了3个重复的ACK。因此当另一个ACK到达时,可以看到cwnd以同样的方式增长,之后降低到1024。图21-10中的时刻21.1也是因为收到了重复的ACK而引起了重传。在重传后收到了3个重复的ACK,因此观察到cwnd增加3个,之后降低到1280。在传输的后面部分,cwnd以线性方式增加到最终值3615。