流水线机制
在前面我们我们介绍TCP数据传输的过程中,讲到发送方发送一个报文段,然后等待接收方的确认报文,当确认无误之后,发送方接着发送下一个报文段。这样一种发送接收的过程是一种停等的(Stop-and-wait operation)。显然,它的传输性能是一个问题,特别是在今天的高速网络中更是如此。
所以,在实际中TCP采用了一种流水线的(Pipelined operation)方式:允许发送方发送多个分组而无需等待确认。毫无疑问,采用这种方式相较于停等有很大的性能上的提升。但是这也对可靠数据传输协议带来了一些影响:
1.必须增加序号范围,因为每一个输送中的分组(不计算重传)必须有一个唯一的序号,而且也许有多个在输送中未确认的报文。
2.协议的发送方和接收方必须能够缓存多个分组。发送方最低限度应当能够缓存那些已发送但还没有确认的分组(便于进行可能的重传)。接收方也需要缓存那些已经正确接收的分组。
流水线机制中的差错恢复
流水线差错恢复和有两种基本的方法:回退N步(Go-Back-N,GBN)和选择重传(Selective Repeat,SR)。
回退N步
在回退N步协议中,允许发送方发送多个分组(当有多个分组可用时)而无需等待确认,但它也受限于在流水线中未确认的分组数不能超过某个最大允许数N。
在上图中显示了发送方看到的一个GBN协议的序号范围。我们将base定义为最早的未确认分组的序号,将nextseqnum定义为最小的未使用序号(即下一个待发送分组的序号),则可以将序号范围划分成4段:[0,base-1]段内的序号对应于已经发送并被确认的分组,[base,nextseqnum-1]段内的序号对应已经发送但未被确认的分组,[nextseqnum,base+N-1]段内的序号能用于那些立即要被发送的分组,如果有数据来自上层的话。最后,大于或等于base+N的序号是不能使用的,直到当前流水线中未被确认的分组得到确认为止。
如图,那些已被发送但还未被确认的分组的序号范围可以看成是一个在序号范围内长度为N的窗口。随着协议的运行,该窗口在序号空间向前滑动,因此,N常被称为窗口长度,GBN协议常被称为滑动窗口协议。
在GBN中,发送方需要响应下列三种事件:
- 上层调用
当上层调用的时候,发送方首先检查发送窗口是否已满,即是否有N个已发送但未被确认的分组。如果窗口未满,则产生一个分组并将其发送,并且更新相应的变量。如果窗口已满,则指示上层该窗口已满。然后上层可能过一会再试。在实际实现中,发送方更可能缓存这些数据,或者使用同步机制允许上层在仅当窗口不满时才调用该服务。 - 收到ACK
在GBN协议中,接收方的发送确认报文采取的是累积确认的方式。发送方收到对序号为n的报文段的确认即表明接收方已经成功接收到了序号为n的以前且包括n在内的所有分组。这时发送方应该更新相关变量,使滑动窗口的起始位置移动到n+1. - 超时事件
当发生定时器超时(可能是该定时器对应的报文丢失,或者是对应的响应报文丢失,或者是时延过大,最终的结果就是发送方在特定的时间内没有收到ACK报文,即定时器超时),发送方重传所有的已发送但还未被确认的分组。(这里的重传所有分组,就对应着滑动窗口的回退)这里的定时器只有一个,它是最早的已发送但未被确认的分组所使用的定时器。如果收到一个ACK,但任然有已发送但未被确认的分组,则定时器被重新启动。如果没有已发送但未被确认的分组,该定时器被终止。
在GBN协议中,接收方的动作很简单。如果一个序号为n的分组被正确接收(即上次交付给上层的数据是序号为n-1的分组),则接收方为分组n发送一个ACK,并将该分组中的数据交付给上层。所有其他情况下,接收方丢弃该分组。
下面我们看一个窗口长度为4的GBN协议的运行情况:
从上图中,我们看到pkt2在发送的过程中丢失了,但是此时发送方没有察觉到pkt2的丢失,滑动窗口也没有满,于是发送方接着就发送了pkt3(发送方发完了pkt3之后,进入了等待的状态,因为此时滑动窗口已满)。pkt3的发送,导致接收方在没有收到pkt2的情况下收到了pkt3(即pkt3失序到达),此时接收方采取的动作就是简单的丢弃pkt3。又回到发送方这边,由于pkt0,pkt1所对应的确认报文的到达,使得滑动窗口向前滑动,发送方就又可以发送pkt4,pkt5了。pkt4,pkt5由于也是失序到达的,所以接收方选择了丢弃它们。终于,发送方对应的pkt2的定时器发生了超时,发送方这时才知道原来pkt2在发送过程中出了问题,发送方重传了此时已发送但未被确认的分组,也就是重传了pkt2,pkt3,pkt4,pkt5。由于接收方接下来接收到的是正确的分组,所以接收方又可以正确处理了。
这就是GBN的一个典型运行实例。
选择重传
GBN协议潜在的允许发送方用多个分组“填充流水线”,因此避免了停等协议中所提到的信道利用率问题。然而GBN本身也有一些情况存在着性能问题。尤其是当窗口长度和线路时延较大时,在流水线中会有大量分组重传。单个分组的差错就能够引起GBN重传大量分组,许多分组根本没有必要重传。
选择重传协议通过让发送方仅重传那些它怀疑在接收方出错的分组而避免了不必要的重传。
下图是在选择回传协议中某一场景下的发送方和接收方看到的序号空间。
在选择重传协议中,在发送方的事件与动作:
- 收到上层数据
当从上层接收到数据后,SR发送方检查下一个可用于该分组的序号。如果序号位于发送方的窗口内,则将数据打包并发送;否则就像在GBN中一样,要么将数据缓存,要么将数据返回给上层一便以后传输。 - 超时
定时器再次被用于防止丢失分组,然而现在每个分组必须拥有自己的逻辑定时器,因为超时发生后只能发送一个分组。 - 收到ACK
如果收到ACK,倘若该分组序号在窗口内,则SR发送方将那个被确认的分组标记为已接收。如果该分组的序号等于send_base(即当前窗口的起始位置处),则窗口基序号向前移动到具有最小序号的未确认分组处。如果窗口移动了并且有序号落在窗口内的未发送分组,则发送这些分组。
接收方的事件与动作:
- 序号在[rcv_base,rcv_base+N+1]内的分组被正确接收
在此情况下,收到的分组落在接收方的窗口内,一个ACK被回送到发送方。如果该分组以前没接收过,则缓存该分组。如果该分组的序号等于接收窗口的基序号(rcv_base),则该分组以及以前缓存的序号连续的分组交付给上层 - 序号在[rcv_base-N,rcv_base-1]内的分组被正确接收
在此情况下,必须产生一个ACK,即使该分组是接收方以前已确认过的分组。 - 其他情况
丢弃分组。
如下是一个选择重传的例子: