可靠数据传输原理

本章将介绍可靠数据传输协议的原理,具体过程为,通过给出越来越复杂的传输服务要求,不断迭代基础的“可靠传输协议”,最终得到真正可用的可靠传输协议。

一、可靠数据传输概述

如图1.1 是可靠数据传输的框架。为上层实体提供到的服务抽象是:数据可以通过一条可靠的信道进行传输。借助于可靠信道,传输数据比特不会受到损坏或丢失,而且所有数据都是按发送顺序交付的。

clip_image001

                                              图1.1 可靠数据传输:服务模型和服务实现

可靠数据传输协议(reliable data transfer protocol)实现可靠传输服务,但其下层协议可能是不可靠的。因此在本节讨论中,我们将底层直接视为不可靠的点对点通信。同时我们将考虑越来越复杂的底层信道,这里贯穿我们始终的一个假设是分组将以它们发送的次序进行交付(即顺序交付),而某些分组可能丢失;这就是说,底层信道不会对分组重排序(在后面,我们统一使用“分组”而不用运输层术语“报文段”)。

在描述中,我们将采用图1.1b中的数据传输协议的接口。调用rdt_send( )函数,上层可以调用数据传输协议的发送方,它将要发送的数据交付给位于接收方的较高层;在接收端,当分组从信道的接收端到达时,将调用rdt_rcv( )。当rdt协议想要向较高层交付数据来,将通过调用deliver_data( )来完成;而rdt的发送端和接收端都要通过调用udt_send( )发送分组给对方。(rdt表示可靠数据传输协议,udt表示不可靠数据传输协议,可见对任何一个开发选好名字是多么重要)。

本节中描述中,仅考虑单向数据传输(unidirectional data transfer)的情况,即数据传输是从发送端到接收端的。不考虑双向数据传输(bidirectional data transfer)。不过需要注意的是,虽然只考虑单向数据传输,但协议也需要在发送端和接收端两个方向上传输分组。

二、构造可靠数据传输协议

现在我们就先通过建立简单的可靠数据传输协议,再提出不同的情况,一步步迭代得到最后的可靠的数据传输协议。

2.1 经完全可靠信道的可靠数据传输:rdt1.0

首先考虑最简单情况,即底层信道是完全可靠的(即比特不会受损或丢失,数据按发送顺序交付)。图2.1显示了rdt1.0发送方和接收方的有限状态机(Finite-State Machine,FSM)的定义。图2.1a中的FSM定义了接收方的操作,b中的FSM定义了接收方操作。需要注意,发送方和接收方有各自的FSM。FSM描述图中的箭头指示了协议从一个状态变迁到另一个状态,图2.1中发送方和接收方的FSM每个都只有一个状态,因此变迁过程必定是从一个状态返回到自身。引起变迁的事件显示在表示变迁的横线上方,时间发生所采取的动作显示在横线下方。如果对一个事件没有动作,或没有就事件发生而采取一个动作,就用∧表示,以分别明确地表示缺少动作或事件。FSM的初始状态用虚线表示。

clip_image002

                                  图2.1 rdt1.0:用于完全可靠信道的协议

如图2.1,rdt的发送端只通过rdt_send (data)事件接受来自较高层的数据,产生一个包含该数据的分组(通过make_pkt (data)动作),并将分组发送到信道中。实际上,rdt_send (data)事件是由较高层应用的过程调用产生的。

在接收端,rdt通过rdt_rcv (packet)事件从底层信道接收一个分组,从分组中取出的数据(通过extract (packet,data)动作),并将数据上传给较高层(通过deliver_data (data)动作)。实际上,rdt_rcv (packet)事件是由较底层协议的过程调用产生的。

在本协议中,所有分组由于是完全可靠信道,接收端不需要提供任何反馈信息,不用担心出错。

2.2 经具有比特差错信道的可靠数据传输:rdt2.0

底层信道更为实际的模型是分组中的比特可能受损的模型,这里我们继续假定所有发送的分组不会丢失,所有分组(虽然比特可能受损)将按其发送的顺序被接收。在当前迭代版本,我们增加肯定确认(positive acknowledgment)和否定确定(negative acknowledgment)两种控制报文,使接收方可以让发送方知道哪些内容被正确接收,哪些内容接收有误并因此需要重传。基于这样重传机制的可靠数据传输协议称为自动重传请求(Automatic Repeat reQuest,ARQ)协议

ARQ协议中还需要另外三种协议功能来处理存在比特差错的情况:

  • 差错检测。事实上除了差错检测,还有纠错技术,这些技术所需的额外比特被汇集在rdt2.0数据分组的分组校验和字段中。
  • 接收方反馈。引入的“肯定确认”(ACK)和“否定确认”(NAK)分组就是为了使接收方能就分组是否被正确接收进行反馈。理论上,这些反馈分组只需要一个比特长:如0表示NAK,1表示ACK。
  • 重传。接收方收到有差错的分组时,发送方将重传该分组。

clip_image003

                                           图2.2 rdt2.0:用于具有比特差错信道的协议

如图2.2的发送端有两个状态。在左边的状态中,发送端协议正等待来自上层的数据,当rdt_send (data)事件出现时,发送方将产生一个包含待发送数据的分组(snpkt),带有校验和,然后经由udt_send (sndpkt)操作发送。在右边的状态,发送方协议等待来自接收方的ACK或NAK分组,如果收到ACK分组,则发送方知道最近发送的分组已被正确接收,因此协议返回到等待来自上层数据的状态;如果收到NAK分组,该协议重传上一个分组并等待接收方为响应重传分组而回送的ACK和NAK。需要注意的是:当发送方处于ACK或NAK状态时,它不能从上层获得更多的数据,即rdt_send ( )事件不可能出现,仅当接收到ACK并离开该状态时才能发生rdt_send ( )事件。因此,发送方将不会发送一块新数据,废除发送方确认接收方已正确接收到当前分组,由于这种行为,rdt2.0被称为停等(stop-and-wait)协议。

rdt2.0接收方的FSM仍只有单一状态。当分组到达时,接收方取决于分组是否受损,来回答一个ACK或NAK。

2.2.1 rdt2.0的修订版:rdt2.1

rdt2.0存在一个致命缺陷,我们没有考虑ACK/NAK分组受损的情况以及纠正ACK/NAK分组的中的差错。ACK/NAK分组受损,可以通过增加校验和比特检测,但更重要的是,当ACK/NAK分组受损,发送方无法知道接收方是否正确接收了上一块发送的数据,就无法进行下一步响应。

对于ACK/NAK受损,我们考虑三种处理办法的可能性:

  • 第一种,当ACK/NAK受损,发送方就不知道接收方表达的意思,这时发送方回复一个新的分组(引入新分组),表达发送方不知道接收方表达的意思,接收方则再次复述刚才的分组内容(重发反馈分组)。但是如果这个新引入的分组也产生了差错呢?那么接收方就不知道发送方要表达的意思,很可能也回复这个新的分组内容,表达接收方不知道发送方的意思。显然,这条路很崎岖。
  • 第二种是增加足够的检验和比特,使发送方不仅能检测差错,还可以恢复差错。对于会产生差错但不会丢失分组的信道,这就能直接解决问题。
  • 第三种是,当发送方收到受损的ACK/NAK分组,不知道接收方意思时,只需要重传当前分组即可。不过这种方法在发送发到接收方的信道中引入了冗余分组(duplicate packet)。冗余分组的麻烦是接收方不知道它上次发送的ACK或NAK是否被发送方正确地收到,因此它无法事先知道接收到的分组是新的还是一次重传。

解决这个问题的办法是在数据分组中添加一个新字段(现在几乎所有的数据传输协议均采用此方法),让发送方对其数据分组编号,即将发送数据的序号(sequence number)放在该字段。这样,接收方就能知道发送方是否正在重传前一个分组(接收到的分组序号和最近收到的分组序号相同),或是一个新分组(序号变化,用模2运算“前移”)。同时因为我们假设信道不丢失分组,所以ACK和NAK本身不需要指明它们要确认的分组序号,发送方知道所接收到的ACK和NAK是为了响应其最近发送的数据分组而生成的。

clip_image004

                                                                       图2.3 rdt2.1 发送方

clip_image005

                                                                        图2.4 rdt2.1 接收方

rdt2.1发送方和接收方FSM的状态数都是以前的两倍,这些状态是为了反映发送发正在发送的分组或接收方希望接收到的分组的序号是0还是1。这里0和1就是分组序号,因为是停等协议,只有发送方确认上一分组被正确接收才会发送下一分组,所以不需要用如1、2、3……来标识分组,只用表明此分组与上一个分组是否同一分组即可。

rdt2.1仍使用肯定确认和否定确认,当接收到失序的分组时,接收方对所接收到的分组发送一个肯定确认(注意:在2.0中我们假设分组按顺序交付,所以这里的失序不是交付失序。它出现的情况是,接收方收到发送方分组后返回ACK,接收方进入下一状态,但是ACK在信道中受损,发送方收到受损的反馈后重发分组,接收方再次收到同一分组,知道ACK反馈出错,因此扔掉这个分组,重新返回ACK让发送方进入下一状态);如果收到受损分组,则接收方返回一个否定确认。如果不发送NAK,而是对上次正确接收的分组发送一个ACK,我们也能实现和NAK相同的效果(因为发送方接收到对同一个分组的两个ACK(即收到冗余ACK(duplicate ACK) )后,就知道接收方没有正确接收跟在被确认两次的分组后面的分组)。

2.2.2 无NAK的可靠数据传输协议:rdt2.2

rdt2.2也是在有比特差错信道上实现的,它与rdt2.1的细微变化是,接收方此时必须包括由一个ACK报文所确认的分组序号(在接收方FSM中,在make_pkt( )中包括参数ACK 0或ACK 1实现),发送方此时必须检查接收到的ACK报文中被确认的分组序号(在发送方FSM中,在isACK( )中包括参数0或1来确认)。

clip_image006

                                                                              图2.5 rdt2.2 发送方

clip_image007

                                                                               图2.6 rdt2.2 接收方

2.3 经具有比特差错的丢包信道的可靠数据传输:rdt3.0

现在我们假定除了比特受损,底层信道还会丢包,但所有分组仍会按其发送的顺序被接收。在rdt2.2中我们使用了检验和、序号、ACK分组和重传,现在我们需要在此基础上解决丢包问题。假定发送方传输一个数据分组,该分组或接收方对该分组的ACK发生了丢失,在这两种情况下,发送方都收不到应当到来的接收方的响应。在当前版本,我们让发送方负责检测和恢复丢包工作。如果发送方愿意等待足够长的时间以便确定分组已丢失,则它只需要重传该数据分组即可。

因此我们至少需要让发送方等待这样一个时间:发送方与接收方之间的一个往返时延加上接收方处理一个分组所需要的时间,来让发送方确定分组已丢失。即如果在这个时间内没有收到ACK,则重传该分组。但也可能发生这样一个情况,一个分组及其ACK都没有丢失,而是经历一个很大的时延,发送方也会重传该分组。这就在发送方到接收方的信道中引入了冗余数据分组,不过这一点已经在rdt2.2中通过序号被解决了。

为了实现这样一个基于时间的重传机制,需要引入一个倒计数定时器(countdown timer)。为此,发送方需要做到:每次发送一个分组(包括第一次分组和重传分组)时,便启动一个定时器;响应定时器中断,采取适当动作;终止定时器。

clip_image008

                                                                     图2.7 rdt3.0发送方

如图2.7是rdt3.0的发送方FSM,因为超时重传机制是在发送方实现的,因此接收方的FSM与rdt2.0的没什么差别。我们看具体的rdt3.0的运行情况,如图2.8,时间从图的顶部朝底部移动,发送方括号部分表明了定时器的设置时刻以及随后的超时。因为分组序号在0和1之间交替,因此rdt3.0有时被称为比特交替协议(alternating-bit protocal)

clip_image009

                                                             图2.8 rdt3.0的运行,比特交替协议

至此我们使用了检验和、序号、定时器、肯定和否定确认分组,解决了比特出错和丢包问题,得到了一个可靠数据传输协议。但该协议是一个停等协议,发送方只有等到接收方正确收到后才会发送下一个分组,发送方利用率(定义为:将分组发送进信道的时间/发送进信道到发送下一个分组间隔时间)太低,因此我们需要一个更高效率的解决方案。

三、流水线可靠数据传输协议

解决停等协议带来的效率问题的一个简单的方法:不以停等方式运行,允许发送方发送多个分组而无需等待确认,如图3.1。因为许多从发送方向接收输送的分组可以被看成是填充到一条流水线中,故这种技术被称为流水线(pipelining)。如图3.2显示了如果发送方可以在等待确认之前发送3分报文,其利用率也基本上提高3倍。

clip_image010

                                                            图3.1 停等协议与流水线协议

clip_image011

                                                               图3.2 停等和流水线发送

要实现流水线协议,需要对可靠传输协议进行改进:

  • 必须增加序号范围,因为每个输送中的分组必须有一个唯一的序号,而且也许有多个在输送中的未确认的报文。
  • 协议的发送方和接收方两端不得不缓存多个分组。发送方最低限度应当能缓冲那些已发送但没有确认的分组,接收方或许也需要缓存那些已正确接收的分组。
  • 所需序号范围和对缓冲的要求取决于传输协议如何处理丢失、损坏及延时过大的分组。解决这些差错恢复的基本方法有两种:回退N步(Go-Back-N,GBN)和选择重传(Selective Repeat,SR)。

四、回退N步

在回退N步(GBN)协议中,允许发送方发送多个分组(当有多个分组可用时)而不需等待确认,但它也受限于在流水线中未确认的分组数不能超过某个最大允许数N。实现GBN协议依赖于一个滑动窗口,GBN也常被称作滑动窗口协议(sliding-window protocol)。

这是一个虚拟的窗口,窗口长度(window size)为N,具体定义为:我们将基序号(base)定义为最早未确认分组的序号,将下一个序号(nextseqnum)定义为最小的未使用序号(即下一个带发送分组的序号),则可将序号范围分成4端。在[0,base-1]段内的序号对应于已发送并被确认的分组;[base,nextseqnum-1]段内对应已发送但未被确认的分组,[nextseqnum,base+N-1]段内的序号能用于那些要被立即发送的分组,如果有数据来自上层的话;最后大于或等于base+N的序号是不能使用的,直到当前流水线中未被确认的分组(特别是序号为base的分组)已得到确认为止。

这里我们将被发送的、未被确认的分组数目限制为N,进行这种限制,是为了对发送方进行流量控制,避免无限制的发送对网络造成拥塞。

clip_image012

                                                                     图4.1 在GBN中发送方看到的序号

在具体实践中,一个分组的序号承载在分组首部的一个固定长度的字段中。如果分组序号字段的比特数是k,则该序号范围是[0,2^k-1]。在一个有限的序号范围内,所有涉及序号的运损必须使用模2^k运算(即序号空间可被看做是一个长度为2^k的环,其中序号2^k-1紧接着序号0)。而在TCP中有一个32比特的序号字段,其中的TCP序号是按字节流中的字节数进行计数的,而不是按分组计数,这一点在讲TCP时会进行说明。

clip_image013

                                                               图4.2 GBN发送方的扩展FSM描述

如图4.2,GBN发送方必须响应三种类型的事件:

  • 上层的调用。当上层调用rdt_send ( )时,发送方首先检查发送窗口是否已满,即是否有N个已发送但未被确认的分组。如果窗口未满,则产生一个分组并将其发送,并相应地更新变量。如果窗口已满,发送方只需要将数据返回上层,隐式地指示上层窗口已满,然后上层可能会过一会再试。在实际中,发送方更可能缓存这些数据,或者使用同步机制(如信号量或一个标志)允许上层在仅当窗口不满是才调用rdt_send ( )。
  • 收到一个ACK。在GBN协议中,对序号为n的分组的确认采取累计确认(cumulative acknowledgement)的方式,表明接收方已正确收到序号为n的以前且包括n在内的所有分组。这一点会在接收方继续说明。
  • 超时事件。协议名“回退N步”来源于出现丢失或时延过长分组时发送方的处理行为。这里定时器将用于恢复数据或确认分组丢失。如果出现超时,发送方重传所有已发送但未被确认的分组所使用的定时器。如果收到一个ACK,但仍有已发送但未被确认的分组,则定时器被重新启动。如果没有已发送但未被确认的分组,停止该定时器。

再来看发送方的FSM描述,在rdt3.0中,因为是停等协议,所以每次发送一个,为这一个分组设置一个定时器。但流水线协议,发送的分组是多个,不可能为每个分组设置一个定时器,没必要而且资源开销太大。因此在rdt_send ( )时,if (base==nextseqnum)判断中启动了一个定时器,它可以被看做是最早的已发送但未被确认的分组所使用的定时器。发送方只使用了一个定时器。

clip_image014

                                        图4.3 GBN接收方的扩展FSM描述

如图4.3,GBN的接收方动作,如果一个序号为n的分组被正确接收到,并且按序(即上次交付给上层的数据是序号为n-1的分组),则接收方为分组n发送一个ACK,并将该分组中的数据部分交付到上层。其他所有情况,接收方丢弃该分组,并为最近按序接收的分组重新发送ACK。注意到因为一次交付给上层一个分组,如果分组k已接收并交付,则所有序号比k小的分组也已经交付,即交付数据是按发送顺序接收的,这也是采用累积确认的原因。

结合发送方和接收方的FSM再看,发送方流水线式发送,接收方按顺序接收,每次接收都是传给上层的分组都是紧接着上一个分组,若不是则为最近按序接收的分组重新发送ACK。发送方收到反馈base=getacknum (rcvpkt)+1,因为返回的序号是最近分组到的序号,所以base不会变就不会等于nextseqnum,表示仍有已发送未被确认的分组,重新启动start_timer。如果发送方收到的反馈分组出错,就什么也不做,等待超时。超时后就发送所有未被确认分组(注意base可能接受到部分确认的序号,因此启动不是从头,而是从第一个未被确认分组开始)。

clip_image015

                                               图4.4 运行中的GBN

如图例4.4,窗口长度为4个分组的GBN协议的运行情况,发送方首先发送分组0~3,然后在继续发送之前,必须等待直到一个或多个分组被确认。当接收到每一个连续的ACK(如ACK 0、ACK 1)是,该窗口便向前滑动,发送方就可以发送新的分组(分组4和分组5)。在接收方,分组2丢失,因此在分组3到达时发现不是与1连续的分组,就丢弃分组3并发送ACK 1(分组1)。在发送方未超时重新发送分组2,并接收到分组2的反馈分组之前,其余到达接收方的分组都会被丢弃。

GBN协议中,接收方丢弃了所有的失序分组,即使该分组是正确的。这样做的原因是,接收方必须按序将分组交付给上层,若分组n丢失而n+1到达了,如果接收方缓存n+1分组,由于GBN的重传规则,n和n+都会被重传。因此接收方直接丢弃n+1分组即可,这样就不必缓存失序,使得接收方维护简单,但实际上很浪费网络。

五、选择重传

选择重传(SR)是为了避免“GBN中单个分组出错或丢失就会引起大量的分组被重传”的问题,选择重传通过让发送方仅重传那些它怀疑在接收方出错的分组而避免不必要的重传。与GBN相同的是,SR也采用窗口来限制未确认的分组。

clip_image016

                                                 图5.1 选择重传发送方和接收方的序号空间

如图5.1,SR的发送方和接收方都有窗口。接收方将确认一个正确接收的分组而不管其是否失序,失序的分组将被缓存直到所有丢失的分组(即序号更小的分组)都被收到为止,这时才可以将一批分组按序交付给上层(按序交付规则)。

SR发送方的事件和动作:

(1)从上层收到数据。当从上层接收到数据后,SR发送方检查下一个可用于该分组的序号。如果序号位于发送方的窗口内,则将数据打包并发送;否则就像在GBN中一样,要么将数据缓存,要么将其返回给上层以便以后传输。

(2)超时。仍采用定时器防止丢失分组,但是与GBN不同的是,现在每个分组必须拥有自己的逻辑定时器,因为超时发生后只能发送该超时的分组。可以使用单个硬件定时器模拟多个逻辑定时器的操作。

(3)收到ACK。如果收到ACK,倘若该分组序号在窗口内,则SR发送方将那个确认的分组标记为已接收。如果该分组序号等于send_base(即窗口内第一个发送的分组被正确接收),则窗口基序号向前移动到具有最小序号的未被确认的分组处。如果窗口移动了并且有序号落在窗口内的未发送分组,则发送这些分组。

SR接收方的事件和动作:

(1)序号在[rcv_base,rcv_base+N-1]内的分组被正确接收。此情况下,收到的分组落在接收方的窗口内,一个选择ACK被反馈给发送方。如果该分组以前没有收到过,则缓存该分组。如果该分组的序号等于接收窗口的基序号,则该分组以及以前缓存的序号连续的分组交付给上层。然后,接收窗口按向前移动分组的编号并向上交付这些分组。

(2)序号在[rcv_base-N,rcv_base-1]内的分组被正确接收到。此情况下,必须产生一个ACK,即使该分组是接收方以前已确认过的分组。

(3)其他情况。忽略该分组。

clip_image017

                                                           图5.2 SR操作

图5.2说明的是SR在丢包时的操作,在该图中,接收方初始时缓存了分组3、4、5,并在最终受到分组2时,才将它们一并交付。

需要注意的是SR接收方的第二步操作很重要,接收方重新确认(而不是忽略)已收到过的序号小于当前窗口基序号的分组。这是很重要的,例如,若发送方分组send_base的ACK丢失,没有从接收方传回发送方,则发送方最终将重传该分组(即使接收方此时已经收到过该分组)。如果接收方不确认该分组,则发送方窗口将永远不能向前滑动。这是很重要到的一个方面,接收方和发送方的窗口并不总一致。

clip_image018

                    图5.3 SR接收方窗口太大的困境:是一个新分组还是一次重传

图5.3是我们要注意的另一个问题——有限序号。在该图例有包括4个分组序号0、1、2、3的有限序号范围且窗口长度为3。假定发送了分组0至2,并在接收方被正确接收且确认,此时接收方窗口落在第4、5、6个分组上,其序号3、0、1。这时有两种情况:第一种如图5.3a,对前3个分组的ACK丢失,因此发送方重传这些分组,那么此时接收方要接收序号为0的分组;第二种情况如图5.3b,前三个分组的ACK被发送方正确接收,因此发送方向前移动窗口并发送第4、5、6个分组,其序号为3、0、1,此时假设序号3丢失,序号0到达。

图5.3中的两种情况,接收方都无法区分是第一个分组的重传还是第5个分组的初次传输。显然这样的窗口长度和序号的安排无法使协议运行,因此需要设置合理的窗口长度,对SR协议,窗口长度必须小于或等待序号空间大小的一半。

解决了窗口大小的问题,还有最后一个问题——重新排序。之前我们假设的是分组不会在信道中被重排序。但现实是链路中存在缓存,可能某个分组由于过大时延,此时发送方已重传并且接收方已确认,在双方的窗口中都已经没有包含该分组时,该分组到达接收方或发送方。对于这样的冗余分组,实际中是通过假定一个分组在网络中的“存活时间”不会超过某个固定最大时间量来做的(不同协议可能实现不同)。

六、总结:可靠传输机制及其用途

(1)检验和:用于检测在一个分组中的比特错误

(2)定时器:用于超时/重传一个分组,可能因为该分组(或其ACK)在信道中丢失了。由于当一个分组延时但未丢失,或当一个分组已被接收方收到,但从接收方到发送方的ACK丢失时,可能产生超时事件,所以接收方可能会收到一个分组的多个冗余版本。

(3)序号:用于为从发送方流向接收方的数据分组按顺序编号。所接收分组的序号间的空隙可使接收方检测出丢失的分组。具有相同序号的分组可使接收方检测出一个分组的冗余副本。

(4)确认:接收方用于告诉发送方一个分组或一组分组已被正确接收到。确认报文通常携带着被确认的分组或多个分组的序号。确认可以是逐个的或累积的,这取决于协议。

(5)否定确认:接收方用于告诉发送方一个分组或一组分组未被正确接收到。

(6)窗口、流水线:发送方也许被限制仅发送那些序号落在一个指定范围内的分组。通过允许一次发送多个分组但未被确认,发送方的利用率可在停等操作模式的基础上的到增加。窗口长度可根据接收方接收和缓存报文的能力、网络中的拥塞程度或两者情况来进行设置。

posted @ 2020-11-26 15:57  Aidan_Chen  阅读(1405)  评论(0编辑  收藏  举报