上一篇中已经有一个可靠数据传输的基本协议——rdt3.0了。这个协议的致命问题是“效率太低了”。如果想让rdt3.0能够使用,我们就必须解决“停等”这个问题。直观的方式就是“允许发送方一次性发送多个分组”。这样就能大大提高物理链路的利用率。

dlan协议 java_窗口大小

这样我们就必须设置更大的位数来表示“编号”,以及更大的缓存空间来缓存更多的分组。 这就是计算机网络中的“流水线技术”。我们只要能在计算机网络中实现“流水线技术”,那么rdt3.0就能实际实现了。在计算机网络中实现“流水线技术”的方法是“滑动窗口协议”。

GBN

GBN协议(回退N步),它允许发送多个分组而不需要等待确认。但它受限制于窗口的大小N。

dlan协议 java_dlan协议 java_02

GBN协议也常被成为滑动窗口协议。在GBN协议中,发送方首先检查窗口大小是否是满的,如果没有满,那么就产生一个分组并将其发送,并且同时更新变量。如果窗口已经满了,那么发送方给上层指出已满。然后上层会等一会再来试。发送方收到ACK应答的方式是“累计确认”。这表明接收方已经正确接受到序列为N的以前的所有分组。当超时事件发生的时候,GBN协议是“回退N步”来进行处理的。它将已经确认收到之后所有已发送但未确认的分组重传。这样能够保证分组的顺序。

接收方需要做的事情比较简单,如果序号为n的分组正确接受,并且它之前的所有分组也是正确接收到了,那么就返回一个ACK,否则丢弃该分组,并且按照最近接受的分组,重新发送该分组的ACK。

GBN的缺点很明显就是重传的时候,需要传输大量的分组,这个问题在网络环境比较糟糕的情形下,会导致非常多的重传出现。一个示例如下

dlan协议 java_rdt滑动窗口协议Java实现_03

在这个示例中窗口大小是4。我们回退4步进行重传,即将2,3,4,5都重新传输一次。

SR

GBN的缺点是明显得,有些分组其实是没有必要重传的。那么为了不进行没必要的重传,我们在接收方设置缓存,让乱序到达的分组缓存起来。这时很明显,发送方只需要重传哪些没有收到ACK应答的分组,这样就必须为每一个分组设置一个定时器。

发送方收到ACK,如果该分组序号在窗口内,则SR标记该分组为已接受。如果该分组序号等于窗口起始位置序号,那么该窗口移动到具有最小序号的未确认分组处,然后发送该窗口内可发送但未发送的分组。

接收方此时也拥有一个窗口,如果分组序号在该窗口范围内,并且以前没收到过,那么缓存该分组,回复一个ACK给发送方。如果分组序号等于窗口起始位置的序号,那么将以前缓存的(小于起始位置)的分组和该分组一起交付给上层。如果接收方收到的序号是在该窗口起始位置的左侧(小于起始位置)。那么需要返回一个ACK。其余情形下,一律丢弃分组。

下面是一个示例。左边是发送方,右边是接收方。

dlan协议 java_窗口大小_04

在SR(选择重传)协议中,我们需要面对的最难的问题是有限序列号,该情形下,可能会产生下面的困境。

dlan协议 java_dlan协议 java_05

接收方无法判断这个序号0到底是之前的分组序号,还是新的分组序号。因此窗口的大小和序号之间的关系必须是合理的。

dlan协议 java_dlan协议 java_06

Ns是发送方窗口大小,Nr是接收方窗口大小,k是序号的位数。