在上一节中我们逐步分析了可靠传输协议的设计过程,最后讲到rdt3.0的设计和实现机制。但是rdt3.0为了实现可靠性,牺牲了很大一部分性能,其中最主要的原因就在于停止等待协议,在发送完数据后,发送端需要等待至少RTT后才能收到ACK,期间什么都不能做。
因此,我们需要设计方案来解决这个问题,这就是本节所讲述的内容。
在之前的停止等待协议中,发送端每发送一个数据包,都会等待至少RTT的时间。现在采用流水线机制,每一次发送端多发送几个数据包,比如上图中的发送3个数据包。即每次发送端发完3个数据包后,再执行停止等待协议,此时的发送方利用率(发送方发送时间百分比)就相当于原先的3倍。
现在利用流水线机制,允许发送方在发送完数据包后,不用立即停止等待,而是可以在发送多个数据包后,再执行停止等待协议,等待每个数据包的ACK反馈。
允许发送方在收到ACK之前连续发送多个分组
左边就是只有一个数据包发送的过程。右图就是流水线机制下,多个数据包的发送和接收。
窗口:用来管理那些发送端已经发出,但还未来得及确认的数据包。
滑动窗口:随着协议的运行,窗口在序列号空间内向前滑动
滑动窗口协议:GBN、SR
如上图所示,窗口左边表示的是已经发送,且已经得到ACK确认的数据分组。 黄色部分表示已经发送,带还没确认的数据分组。蓝色则表示还可以使用的序列号范围(最大为N,蓝色表示接下来发送可以使用的序列号范围)。当蓝色部分用完后,就必须等待窗口内的数据包得到ACK确认后,窗口继续右移,来发送加下来的序列号数据包。
接下来,我们分别对GBN和SR两种滑动窗口协议进行介绍,介绍其实现原理和区别,便于我们理解滑动窗口协议的改进。
发送方
如上所述,窗口左边的绿色部分表示已经获得ACK确认的数据分组。黄色的表示已经发送,但未ACK确认的分组,蓝色部分表示继续发送分组可使用的序列号范围,比如此时如果需要再发送一个数据分组,使用的序列号就是图中nextseqnum对于的序列号值。最右边的白色是还不能使用的序列号,需要等待窗口滑动右移才能到达。
累计确认机制
对于GBN协议来说,发送端对于ACK的确认采用的是累计确认的方式。
GBN发送方的有限状态机示例如下图所示:
if(nextseqnum < base+N)
表示如果下一个序列号小于窗口的最大范围,说明还可以继续发送数据包,则取nextseqnum的序列号来构造数据包并发送。同时,如果在窗口的最左侧时,启动当前窗口内流水线发送数据包的定时器。可以看到,整个窗口内发送数据包时,只会有一个定时器。refuse_data
:如果窗口内的序列号已经用光了,也就是发送的数据包序列号达到了窗口的最大值。此时,发送方再需要发送数据包时,会被拒绝refuse。timeout
:如果触发了timeout超时,则会从窗口最左侧的数据包开始,逐个重发,知道窗口最右端。rdt_rcg && notcorrupt
:由于是累计确认,在收到ACK(n)后,表示认到序列号n(包含n)的分组均已被正确接收,此时起始偏移base会更新为所收到的确认号ACK(n)的值+1(n+1)。这里,也就是窗口滑动的原理过程了。GBN接收方的有限状态机示例如下图所示:
ACK机制:发送拥有最高序列号的、已被正确接收的分组的ACK(累计重传机制)
乱序到达的分组:
假定窗口的大小是4,所以在发送方可以发送pkt0、pkt1、pkt2、pkt3,发送完pkt3后达到了窗口的最大序列号,因此发送端开始停止等待ACK反馈。
前两个pkt0、pkt1都正确到达了分组,pkt2在发送过程中丢失了,而pkt3也正确到达了接收端。在接收端接收到pkt0、pkt1之后,接收端期望的序列号expectedseqnum应该为2,而此时pkt3分组到达了接收端,但不是接收端期望的序列号。按照上面的分析我们知道,此时接收端会丢弃序列号3对应的分组,然后发送一个ACK1的返回(因为期望序列号是2,所以发送ACK1表示序列号2前面的数据分组都收到了)。
但是这个ACK1在返回时也不幸丢失了。由于pkt1的分组和该分组的ACK1的反馈被成功接收到了,此时发送端的窗口就可以向右移动两位,有了序列号来发送pkt4、pkt5。
在发送完pkt4、pkt5之后,由于pkt2的ACK一直没有收到,所以发送端触发了timeout超时,又由于上一次接收到的最大ACK确认序列号为1,即序列号为1和1之前的都已经确认了,所以发送方会重发序列号为1之后的分组,即上图中的pkt2、pkt3、pkt4、pkt5。对于接收方收到的重复分组,会根据序列号进行去重,所以不怕分组重复。
练习题: 数据链路层采用后退N帧(GBN)协议,发送方已经发送了编号为0~7的帧。当计时器超时时,若发送方只收到0,2,3号帧的确认,则发送方下需要重发的帧数是多少?分别是哪几个帧?
解: 根据GBN协议工作原理,GBN协议的确认是累计确认,由于发送端已经得到的最大ACK确认序列号为3,表示序列号3和之前的分组都已经确认(即使ACK1的确认序列号可能在反馈途中丢失了)。因此,发送方需要重新下发的帧数是4个,依次分别是4、5、6、7号帧。
GBN有什么缺陷?
通过上面的介绍也能发现,GBN一个比较明显的缺陷就是,当需要重传时,可能一次会重传很多个分组,比如当某个序列号n的分组丢失时,发送端会重传序列号n和n之后到窗口右侧最大序列号之间的数据分组。 这会导致网络中充斥着很多这些不必要的重复分组,造成网络拥塞,性能变差。所以一个显然的改进就是,我们不使用累计确认机制,而是对每个分组单个确认,同时不丢弃那些乱序的分组,接收端缓存起来。
接收方
发送方
SR协议中发送方和接收方的窗口如下所示:
图(b)表示的是接收方的窗口。接收方的窗口大小也是N,窗口前面是那些已经按序到达成功交付的数据分组。窗口里的灰色部分表示接收方期望收到的,但是还没收到的分组序号。红色的则表示那些乱序到达的分组,这些乱序到达的分组,此时接收方会缓存起来,并返回ACK。蓝色的部分表示接收方可以接收的序列号范围。
从上两个图可以发现,在SR协议中,发送方和接收方的窗口不是同步的,发送方的窗口可能是滞后于接收端的(因为可能某些已发送的分组还没收到ACK)。
上图给出了a、b两种场景,图中窗口大小是3,序列号只包括0、1、2、3四个序列号,循环使用。思考接收方能区分开上面两种不同的场景吗?
那么,在(a)场景中发送方重发分组0,接收方收到后会如何处理?
从上帝视角我们直到,这两种场景是不一样的,(a)场景中的pkt0是重传,而(b)场景下的分组是正常的数据分组。但是接收端并不能区分这两种场景,就有可能导致错误。比如在(a)场景下,接收端可能以为这次收到的pkt0是正常的数据分组进行了接收,并按序向上交付。但此时,这个分组并不是正确按序到达的分组,就会导致上层数据解析错误。
之所以会导致上述的问题,显而易见,是由于我们的序号太少,而窗口尺寸相对序号又比较大。因此在SR协议中,需要考虑序号个数与窗口大小之间的关系。
序列号个数与窗口尺寸需满足什么关系?
Ns+Nr <= 2k
上式中k表示序列号的位数,Ns表示发送方窗口尺寸,Nr表示接收方窗口尺寸。