章节五:可靠数据传输原理

在上一节运输层介绍和UDP介绍了运输层的作用和UDP不可靠数据传输协议,本节来学习下可靠数据传输的相关内容。

1 可靠数据传输原理

在之前我们了解UDP是不可靠数据传输服务,其不保证数据能正确由发送端交付给接收端,这是因为网络层及以下提供的服务不是可靠数据传输,那么如果在运输层实现数据可靠传输就需要做额外的很多工作,在这一节我们脱离分层的约束,我们讨论以下几种情况:

  1. 经完全可靠信道的可靠数据传输;
  2. 经具有比特差错信道的可靠数据传输;
  3. 经具有比特差错丢包信道的可靠数据传输;

我们在来讨论在以上三种情况下,我们需要做什么,同时我们也只是讨论数据由发送方到接收方的数据发送,也就是单向数据传输。接下来我们来对这三种情况分别进行讨论。

2 经完全可靠信道的可靠数据传输

首先我们来讨论最简单的情况下,也就是我们是基于一个完全可靠信道来构建可靠数据传输,这里称这个协议为rdt1.0(reliable data transter protocol,可靠数据传输协议)。如下:
在这里插入图片描述
上图展示了经完全可靠信道构建的可靠数据传输发送端和接收端的状态,首先来说一下这个的各个部分代表什么意思,图中的圆指的是发送方或者接收方所处的状态,其中的箭头指的是状态转变,箭头中横线上方是发生的事件,而横线下方是在事件发生后所做的事情,如果什么也不做的话使用“-”指示。我们来看在rdt1.0协议下发送端的状态转换,可以看到发送端只有一个状态,就是等待上层(应用层)的调用,当上层调用rdt_send(rdt_send指运输层提供给应用层的可靠数据传输的接口函数)时,将数据封装成报文段(packet),然后使用更下层的udt_send发送数据(udt_send是指下层的不可靠数据传输)。因为我们在假设的是经可靠数据传输信道的前提下,所以运输层不需要考略差错丢失等问题。而在接收端也是只有一个状态,就是一直监控下层数据接收,当接收到数据后,将数据抽取然后交付给上层。

3 经具有比特差错信道的可靠数据传输

在上面是假设是在完全可靠信道构建的可靠数据传输,但是大多数情况下,由于分组数据在传输中首外部因素原因会发生比特差错,从而导致发送的数据和接收的数据不一致,我们在UDP讨论了如何校验具有比特差错的数据,可以通过传输一个校验和来实现,那么对于具有比特差错的数据我们该如何处理呢?我们还是以打电话的示例来说,假设在确认双方的身份后,A需要和B交代三件时,当A说完第一件事后,B为了让A了解我知道这件事了,可以通过向A说“ok,我了解了”,然后紧接着A再交代B第二件事,但是在交代时可能由于周围环境操作或者网络信号不好导致B没有听清楚,所以此时B可能会说“不好意思,我这边没有听清楚,麻烦您再说一下吧!”,A在收到B的重复要求后会重复交代第二件事,那么在A交代完后,B还是会回复“ok,我了解了”从而让A了解B已经知道这件事了,当然第三件事交代完B同样也会回复“ok,我了解了”。相似的我们可以通过这种肯定确认(“ok,我了解了”)和否定确认(“不好意思,我这边没有听清楚,麻烦您再说一下吧!”)针对具有比特差错的数据的传输,这种实现就需要提供如下几个功能:

  1. 差错校验:在UDP时我们了解了UDP是通过校验和来实现对具有比特差错的数据进行校验的,响应的我们也可在传输数据时在首部添加校验和来功接收端对数据进行差错校验。
  2. 接收方反馈:通过上面的例子我们知道发送方是需要接收到接收方的反馈的,那么接收方在接收到数据后,可以将反馈数发送给接收方,因为只有两种状态ACK(肯定确认)和NAK(否定确认),所以可以通过一个比特来标识,1表示ACK,0表示NAK。

那么接下来来看一下rdt2.0协议的发送方和接收方的状态变化,如下图:在这里插入图片描述
在这里插入图片描述
如上图,rdt2.0的发送方有两个状态,左边的状态等待上层的调用,当上层发生调用后,其通过make_pkt方法将上层数据和校验和checksum封装成报文段,然后调用udt_send将数据发送,数据发送后发送端状态就变为等待刚才发送的数据的ACK或者NAK,当接收方向发送端发送NAK,则代表发送端刚才发送的数据具有比特差错,需要重新发送,那么发送端调用udt_send将刚才的数据重新传输,然后继续等待发送数据的ACK或者NAK,当接收端返回一个ACK,那么其状态变为等待上层的调用发送数据。在接收端则只有一个状态就是等待下层的调用,当下层接收到数据后,对数据进行校验,当通过校验后,组织ACK响应给发送端,当接收的数据不通过校验的话则向发送方发送NAK要求发送方重传数据(这里的corrupt和notcorrupt函数用于来检验数据分别是校验不通过和校验通过)。
在rdt2.0我们需要注意发送端的状态,当发送端发送数据后,其状态装换为等待ACK或者NAK,只有收到接收端的ACK然后再次发送上层的数据,由于这种状态,所以rdt2.0被称之为停等协议

在上面的rdt2.0协议,似乎运行时没什么问题的,但是我们忽略了一个重要的问题,我们是假设在具有比特差错信道构建可靠数据传输,但是在接收端发送ACK或者NAK时,这个确认数据发生差错,那么发送端就不知道接收端是否正确接收数据。那么考虑ACK和NAK确认数据差错可能有有以下三种情况:

  1. 还是以上面的打电话示例来说,当A在交代第二件事时,由于其他原因B没有听清楚,那么B会说“不好意思,我这边没有听清楚,麻烦您再说一下吧”,但是由于其他原因,A也没有听清楚B的回答究竟是接收到了还是再复述一遍,那么A会说一句“你说什么”,这样在rdt2.0就引入了发送端对接收端的确认,但是会存在双方都没有听清楚,B会在收到A的“你说什么”不能知道是发送的新数据还是之前的复述,那么会再次确认,如此反复陷入一个死循环。
  2. 在接收端可以针对ACK和NAK的响应数据添加足够的检验和,不仅能使接收方对ACK和NAK数据进行校验,同时也可将具有比特差错的ACK和NAK数据进行恢复,这样做无疑可以解决。
  3. 还有一种方法就是当发送方接收到含糊的ACK或者NAK分组时,重传当前的分组,那么这样就会引入冗余分组。冗余分组确实可以解决在ACK或者NAK发生比特差错时的情况,但是在接收端就比较难了,因为接收端不会到收到的数据是重传的数据还是新发送的数据。

那么解决第三种方法的问题,可以通过序列号对每个分组进行标识,那么在发送报文中添加序列号用于标识报文,那么在接收方可以通过序列号对数据的判断是重传数据还是新发送的数据。对于rdt2.0的停等协议,使用一比特用于标识数据的序列号即可,即0和1,那么针对rdt2.0进行优化为rdt2.1,相应的发送端和接收端的状态都是原来的两倍,这是因为在发送方和接收端都需要分别判断序列号为0和1的数据状态,同样在接收端的ACK或者NAK不需要标识这是对那个序列号数据的确认,因为我们假定的是不丢失分组,只是数据比特可能存在差错,同时也是停等协议。rdt2.1协议发送端和接收端的状态如下:

rdt2.1 发送端:
rdt2.1 发送端
rdt2.1 接收端
rdt2.1 接收端
接下来就来说明一下rdt2.1协议的发送端和接收端的状态转换及事件。首先来说接收端,假设最开始接收端处于等待上层的调用0,那么当上层通过rdt_send方法调用数据发送后,那么发送将数据,确认号(这里为0)以及校验和checksum组装成报文段然后调用下一层的udt_send方法发送,当数据发送完成后接收端状态变为等待ACK或者NAK0,这里与rdt2.0一样当数据发送完成后需要等待接收端对该数据的确认。在该中状态有三种情况:第一种情况就是接收端接收到的数据比特有差错,那么返回一个NAK,同时这个确认的NAK没有比特差错,那么发送再次调用udt_send将序列号为0 的数据再次发送;第二种情况就是收到确认了,但是确认数据通过校验和检查有比特差错,那么发送端就无从知道接收端是否接收到了正确的数据,所以再次调用udt_send将序号为0的数据再次发送,那么这种情况下接收端有两种情况,就是确实为ACK和NAK,但是数据在发送给发送端时发生比特错误,所以在发送端使用冗余分组重发序号为0的数据。当处于等待ACK或者NAK0的状态接收到接收端发送的确认并且没有比特差错,那么发送端状态就变为等待上层的调用1,接下来的状态改变与之前的描述一致,只是在发送数据的序号不同,就不再次赘述。
接下来来看接收端的状态转换。假设接收端首先处于等待下层的0,也就是等待序号为0 的数据。在此状态接收端只有在接收到序号为0的数据才会解析并分发到应用层然后将状态变为另一个状态(等待下层的1),而遇到序号为1的数据,接收端只是在接收到数据发送一个确认ACK,这个序号为1的数据我们称之为失序的分组数据。同时对于数据比特有差错的数据发送一个NAK。也就是只有在数据没有差错同时接收的数据的序列号于正在等待的序号一致时才会解析数据并分发给应用层。这里可能有一个疑问,为什么针对失序的数据,也需要发送一个确认ACK?我们假设有这么一种情况,发送端发送了序列号为1的数据,然后进入到等待ACK和NAK1状态,相应的接收端收取到数据并且数据无比特差错,接收端将数据解析并分发到应用层,并发送一个ACK响应,同时接收端进入等待下层的0,不幸的是接收端的响应数据在传输过程中发生了差错,那么发送端在判断确认数据存在比特差错,所以重传了序号为1的数据,同时又将状态变为等待ACK或者NAK1,此时接收端收到了冗余分组,那么如果接收端不发送确认,那么接收端会一直在等待下层0一直等待,因为发送端接收不到任何ACK或者NAK,那么会一直在等地ACK或者NAK1状态下,那么双方会一直等待下去。
针对rdt2.1使用了从接收方到发送的肯定确认和否定确认。当接收方收到一个失序的分组,接收方对所接受的分组发送一个肯定确认。如果收到一个受损的分组则发送一个否定的确认。那么我们可以不发送NAK,而是对上一个正确接收的分组发送一个肯定的确认,那么需要在rdt2.1中的判断ACK时使用的isACK()添加一个确认的序号,则可以完成,那么对rdt2.1优化为rdt2.2如下,为一个无NAK的协议:

rdt2.2 发送端
在这里插入图片描述
rdt2.2 接收端
在这里插入图片描述
如上图所示,展示了rdt2.2协议的发送端和接收端,在发送端在等待ACK时,如果收到的确认数据存在比特错误或者确认的是上一个序号的数据,那重发当前发送的数据,只有收到对当前发送数据的确认才会转换状态继续发送下一个序号数据。在接收端,当数据存在比特错误则发送一个确认ACK,但是这个确认ACK指定了确认的数据序列号,也就是上一次正确接收的数据的确认号。

4 经具有比特差错的丢包信道的可靠数据传输

在2和3中讨论的是在没有丢包信道上构建的可靠数据传输,那如果在可能发生丢包的信道上构建可靠数据传输,我们应该注意什么?(因特网中丢包情况是存在的)。首先我们来看会发生什么,假设在在发送端发送了一个数据,在接收端正确的接收到了该数据,同时发送一个对该数的确认数据,但是这个确认数据在发送过程中丢失了,那么发送端会一直等待接收端的ACK,而接收端会一直等待下一个序号数据的到来,那么发送端和接收端会一直等待,那么双发就会卡死在这里,那么我们就是关注的的两个问题就是:如何检测丢包以及丢包后如何处理,通过3的rdt2.2提供的校验和、序列号、ACK和重传可以处理丢包后的情况,那么如何检测是否发生了丢包呢?一般发送方只要愿意可以等待足够长的时间来确定确实发送了丢包,那么发送方只要像rdt2.2那样重发分组即可。
那么就可以通过超时重发来处理丢包的情况,那么这个超时重发的超时时间应该为多久呢?那么超时重发的时间最小要大于发送端和接收端的时延,如果超时重发时间小于发送端和接收端时延时间,那么对于能正常接收和确认的数据也会重发,很明显这不是我们想要的,那么这个超时重发时间又不能设置的太长,如果太长又会影响效率,那么超时时间应该设置一个合理的时间,后续我们再讨论。那么针对丢包情况,又会存在这种情况,就是在发送端发送数据后,开启定时器,接收端正确接收到数据,但是发送的ACK丢失,那么在发送端到达超时时间后,会重发数据,那么会在成冗余分组数据,但是幸好的是rdt2.2通过序列号等已经解决冗余分组的问题。
既然有了超时重传的思路,那么我们在数据发送后开启一个定时器,当定时器超时后认定为发送数据丢失,则重传数据并再次开启定时器,当正确收到接收端的ACK后则停止定时器,并进入下一个状态。如下是rdt3.0的发送端的状态变化:
在这里插入图片描述

5 流水线可靠数据传输协议

我们在之前讨论的rdt3.0协议已经提供了可靠数据传输的服务,接下来我们来讨论以下基于rdt3.0协议的性能问题。假设有两个主机A和B进行通讯,同时A和B两个主机物理距离相距较远,数据在两个主机之间传播的往返时间为RTT,两个主机通过一条发送速率为R的信道相连。两个主机发送的数据长度为L(这里的RTT是指 数据在传输介质上的传播速传播长度,而发送速率是指 数据长度 ÷ (数据最后一个比特进入信道时间 - 数据第一个比特进入信道时间))。发送速率如下:
在这里插入图片描述
那么发送速率就为:
在这里插入图片描述
这里我们假设RTT=30ms, L=1000字节(8000比特),R=1Gbps(每秒传输10^9比特),那么这个分组数据完全发送到信道所用时间为:
在这里插入图片描述
我们知道rdt3.0是一个停等协议,当其发送一个数据后只有等到收到指定的ACK或者超时后才会发送数据,那么我们来看一下其效率如下,还是基于上面的假设,当发送端A发送一个分组后,那么在0.008ms后分组数据的最后一个比特进入信道,然后数据在信道中传输,上面假设RTT为30ms,那么数据到达接收端B则使用时间为15ms + 0.008ms,在B收到数据后发送一个ACK,这里假设ACK数据很小,忽略其传输时间(数据由主机传输到信道的时间),那么发送端A接收到这个ACK所用时间为30ms + 0.008ms。我们定义发送发的利用率为:发送方忙于将数据发送到信道的时间与发送时间之比,那么利用率U为:
在这里插入图片描述
很明显发送方只有在万分之2.7的时间是忙碌的,而其他时间则是在等待。即使发送速率为1Gbps,很明显这是由于上层的网络协议限制了底层链路和物理层的能力。这是我们在没有考虑丢包和其他中间路由排队的时延。

那么如何解决这种问题呢?就是通过不停等的方式运行,我们可以让发送发同时发送多个分组而无需等待,那么其利用率也会相应的提高,这样的方式称之为流水线。如下图:

停等操作:
在这里插入图片描述
流水线操作:
在这里插入图片描述
上述的流程已经描述的很明白,我们针对流水线操作提出几个问题:

  1. 由于rdt3.0的为停等操作,那么其发送的数据只需要一个比特0和1来表示序列号即可,但是流水线操作同时发送多个分组,那么分组数据就需要一个唯一的标识,用于确认使用。那么就需要增加序列号范围。
  2. 发送端和接收端都需要缓存多个分组,发送端同时发送多个分组,那么需要等待多个分组每个都被确认,如果丢失或者比特差错,那么需要重传,所以发送方需要缓存发送的多个分组,直到这个分组被确认。在接收方,接收端需要缓存正确接收到的多个分组。
  3. 流水线需要针对差错做恢复,那么如何进行丢失或者差错数据做服务,有两种基本方法:回退N步和选择重传。

6 回退N步

在5中讨论了使用流水线操作,发送方一次可以发送多个分组,那么一次发送的分组数是无限的吗?很明显不是,在上一节讨论了,发送方需要缓存没有确认的分组,那么如果一次发送的分组数据为无限个,那么发送方需要足够大的存储空间来缓存这些未被确认的分组,所以流水线操作每次发送的分组数受限于N。那么对于回退N步协议(Go-Back N,GBN)的发送方需要维护下面的序号:
在这里插入图片描述
如上图所示,发送方需要维护序号用于标识每次发送的分组的,上面我们讨论了流水线操作,那么一次最多发送分组为N,这个N称之为窗口长度。发送方维护基序号(base)状态用于记录发送方发送的分组未被确认的最小序列号,而下一个序号(nextseqnum)则为发送方发送新的分组用于指定的序列号。当序列号到达base + N时,那么由于达到最大传输分组数,所以此时拒绝发送数据,那么随着发送方接收到接收端传过来的ACK,相应的base数增加,那么这个长度为N的窗口在序号上向前滑动,所以GBN也称之为滑动窗口协议。那么在接收端怎么处理呢?在接收端还是同rdt3.0一样,采用累积确认,也就是确认ACK是接收端成功接收的最大序列号。下图是GBN协议的状态转换图示:

GBN协议发送端:
在这里插入图片描述
GBN协议接收端:
在这里插入图片描述
如上图所示,GBN协议在发送方发送完n个分组后,那么等待接收端的批量确认,同时开启计时器用于处理数据丢失和超时,没接收到一个确认,则将接收到的确认号+1赋值给基序号(base),当出现超时情况,则重发base到nextseqnum-1的分组。发送端在接收到有比特差错的ACK数据是不处理的。只是等待超时重传。那么在接收端,采用累积确认的方法,没接收到一个分组,则发送ACK,发送的ACK是接收端正确接收的最大序列号。当接收端接收到一个有比特差错的分组,则接收端发送上一个(也就是最大正确接收序列号)序列号。在接收端是不需要缓存失序的分组的,因为发送端会重传未确认的分组及其后的所有分组。当然这样确实会引入过多数据传输,因为正确传输的失序分组还会重新传输,可能在重新传输过程中,数据丢失或者发送比特错误,那么需要再次重传。针对这种问题我们在下节来讨论。

7 选择重传

在GBN协议中,当发送方发送多个分组后,等待多个分组的确认,每当收到一个最小的未确认序列号,则base+1,也就是发送窗口向前移动,当某一分组超时后,则重传该分组及后续所有发送的分组,即使这些分组中有的分组已经被接收端正确接收了,设想一种最坏的情况,发送端发送了100个分组,但是这些分组均被接收端正确接收,只是确认ACK丢失,那么当超时后,发送端重传这100个分组,但是这次重传部分分组被正确丢失,部分有比特错误,那么超时后又会导致已经接收的分组再次被重新传输,同时网络也会被很多冗余分组充斥,最坏的情况就是发端一直在重传这100个分组,而新到来的分组无法被传输,存较大的性能问题。那么如何解决呢?问题既然是由于传输冗余的分组(已经被确认的分组),那么在接收方就需要记录每个方的分组的确认状态,重传时只需重传发送方认为出错的分组(发送分组丢失、发送分组发送比特错误或者确认ACK丢失或者错误),同时发送方需要为每一个发送的分组维护一个定时器。在接收端,接收到失序后的分组进行缓存,当所有按序到达的分组全部到达后则将数据统一交付给上层。那么在接收端也会维护一个滑动窗口,但是接收端和发送端的滑动窗口状态是不一致的,如下:
在这里插入图片描述
如图所示,在发送端在滑动窗口中存在发送并且是确认的,但是这种分组时失序的分组,发发送端记录上每个发送的数数据的状态,当然,这些被发送的分组时位于滑动窗口范围的分组。那么这样的话在发送端会维持以下几个状态:

  1. 从上层接收数据。从上层接收的数据后判读是否有可用的序列好,如果有则为该分组分配一个序列号发送,同时为该分组维护一个定时器用于对该分组丢失或者差错而重传。当没有可用的序列号时,执行拒绝方法或者进行缓存,待后续也可用序列号时再发送。
  2. 超时。与GBN协议不同,选择重传不再是重传未确认及未确认分组后的所有分组,而是只重传未被确认的分组。
  3. 收到ACK。如果收到ACK,判断该分组ACK的位置,如果ACK位于滑动窗口内,则将该分组状态修改为已确认,如果该分组的序号等于send_base,那么将send_base向前移动至最小未确认分组的序号,同时判断是否有未分配序号的分组,如果有则分配序号并发送分组。

上述是选择重传协议的几个状态的转换,那么接下来看看接收端的处理:

  1. 当接收的分组位于滑动窗口。如果是一个失序的分组,则缓存该分组并为该分组发送一个ACK,如果接收的分组正好为rcv_base,则将rcv_base前及rcv_base分组统一交付给上层,同时将接收窗口向前移动。
  2. 序号位于[rcv_base-N,rcv_base-1]的分组,则需要发送一个ACK,因为发送端和接收端的窗口状态是不同的,所以发送ACK需要让发送端的窗口向前移动。
  3. 其他分组情况则忽略。

到这里我们再来考虑一个问题,假设我们的接收窗口的长度为3,而序号范围为0到3,那么看以下两种情况:
情况1:
在这里插入图片描述
情况2:
在这里插入图片描述
在这两种情况下,第一种的分组0为重传,而第二种的分组0为新发送的,但是在接收端,其无法判断这个分组0究竟是重传的还是新发送的。所以为了解决这种情况,所以需要窗口的长度要小于等于序号范围长度的一半。

本章节主要是通过假设迭代从而来探讨可靠数据传输的实现原理,下一节来了解TCP可靠数据传输的相关内容:面向连接的传输:TCP

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值