你还没搞懂可靠数据传输协议?

在开始之前,欢迎大家关注我的公众号,跟我一起交流学习。

可靠数据传输作为计算机网络的“TOP-10”问题,一直是面试常见问题。本文将简述可靠数据传输的原理,并渐进地构造一套可靠传输协议。

可靠数据传输原理

所谓可靠数据传输,简单来讲,就是指发送方发送的数据与接收方接收到的数据完全相同。而完全相同实际就是要求数据不错误(无损坏)、不丢失(无间隙)、不重复(非冗余)、不乱序(按序)。

上图以运输层为例描述了可靠数据传输的框架。运输层为应用层提供可靠数据传输服务,即应用层只管把数据丢给运输层,至于能不能可靠传输过去以及怎么实现可靠传输,应用层不关心。且由图b)可以看到,运输层的下层——网络层,也可能是不可靠的。所以这种可靠数据传输服务是由运输层的可靠数据传输协议来保证实现的。

以下使用rdt代表可靠数据传输(reliable data transfer)

构造可靠数据传输协议

rdt1.0:底层信道完全可靠

在rdt1.0中,我们假定底层信道完全可靠。下图显示了rdt1.0发送方和接收方的有限状态机

有限状态机:Finite-State Machine, FSM。

虚线箭头表示FSM的初始状态;实线箭头表示FSM从一个状态变迁到另一状态;横线上方是引起状态变迁的事件,横线下方是事件发生时采取的动作。


在rdt1.0发送方,当上层调用rdt_send(data)时,状态变迁事件触发,rdt1.0协议对上层传来的数据data进行一些封装,得到packet,然后调用udt_send(packet),由底层信道将packet传输到接收方。由于rdt1.0中底层信道完全可靠,所以执行完这些动作后,发送方的FSM直接返回初始状态,等待上层的下一次调用。

在rdt1.0的接收方,当下层调用rdt_rcv(packet)时,状态变迁事件触发,rdt1.0协议对下层传来的数据packet进行解封装,重新得到data,然后调用deliver_data(data),将数据传给其上层。由于rdt1.0中底层信道完全可靠,所以执行完这些动作后,接收方的FSM也直接返回初始状态,等待下层的下一次调用。

在rdt1.0中,由于假定底层信道完全可靠,所以把数据交给它后,可以放心地返回初始状态,而不用接受任何反馈信息,因为数据一定会可靠地传输过去的。

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

在rdt2.0中,我们假定底层信道仅可能发生比特差错,即可能发生0变1或1变0。下图显示了rdt2.0发送方和接收方的有限状态机。

rdt2.0的思想是既然底层信道会发生比特差错,那么就让接收方在接收到数据后,给发送方反馈一个信息,根据这个反馈信息,发送方就能知道刚才发送的数据发没发生比特差错,发生了就重传刚才的数据,没发生就回初始状态,等待下一次上层的调用。

也就是说rdt2.0发送方的FSM要有两种状态了:一是等待上层调用,二是等待接收方的反馈信息。

在rdt2.0发送方,当上层调用rdt_send(data)时,状态变迁事件触发,rdt2.0协议把上层传来的数据data进行封装,得到sndpkt,然后调用udt_send(sndpkt),由底层信道把sndpkt传到接收方。

在这之后,由于底层信道可能发生比特差错,所以rdt2.0发送方不返回初始状态,而进入等待ACK或NAK状态。如果接收方反馈ACK,说明刚才发送的数据没有发生比特差错,发送方返回初始状态;如果接收方返回NAK,说明刚才发送的数据出错了,这时候rdt2.0协议重新调用udt_send(sndpkt),即把刚才的数据重传一次,之后继续进入等待ACK或NAK状态,如此往复,直至数据无错传输至接收方。

与rdt1.0的封装不同之处是,rdt2.0对data的封装加入了校验和(checksum),这就是差错检测的具体实现方法。

在rdt2.0接收方,下层调用rdt_rcv(rcvpkt)并调用corrupt(rcvpkt)和notcorrupt(rcvpkt)进行差错检测。如果发生了比特差错,则反馈NAK给发送方;如果没发生比特差错,则提取出data传给上层,并给接收方反馈ACK。

rdt2.0的特点:
相比于rdt1.0,rdt2.0引入了

  1. 差错检测机制(使用校验和实现)。
  2. 接收方反馈机制(接收方反馈ACK或NAK给发送方)。
  3. 重传机制。

rdt2.0的缺陷:

  1. 属于停等协议。即在发送方处于等待ACK或NAK状态时,发送方的rdt_send()事件无法触发以发送一个新数据。发送方下来,到接收方已正确接收当前数据,才返回初始状态,才可进行下一批数据的发送。
  2. 没有考虑到ACK或NAK受损的情况。即,发送方无法正确接收接收方的反馈信息的情况。

处理ACK或NAK受损的简单粗暴的方法:如果无法正确收到ACK或NAK,发送方直接重传当前数据。

但这样的后果是会产生重复分组(冗余分组)。那就要解决冗余分组问题,方法是在数据分组中添加一个新字段——序号。由此,我们rdt2.0的一个修订版——rdt2.1。

rdt2.1:解决冗余分组问题

序号字段:让发送方对其数据分组编号,接收方只需要检查序号即可确定收到的分组是新数据还是重传的数据。

针对rdt2.0这种停等协议,无非就是重传当前分组和传新分组这两种可能,所以只需要0,1两个序号就够用了。下图显示了rdt2.1发送方的有限状态机。

在rdt2.1发送方,上层调用rdt_send(data),状态变迁事件触发,rdt2.0协议把上层传来的数据data进行封装,得到sndpkt,然后调用udt_send(sndpkt),由底层信道把sndpkt传到接收方。

在这之后,发送方进入等待ACK或NAK 0状态。如果接收方的反馈信息发生了比特差错或者反馈信息是NAK,发送方重传当前分组;如果接收方的反馈信息没有发生比特差错并且反馈信息是ACK,发送方则进入等待来自上层的调用 1状态,准备发送新分组(序号为1)。对新分组的操作就跟0一样了,此处不再多言。

与rdt2.0不同的是,rdt2.1对data的封装加入了序号。这是解决重复分组问题的具体实现方式。

下图显示了rdt2.1接收方的有限状态机。

在rdt2.1接收方,若收到的分组无比特差错是期望接收的分组(0)时,rdt解封装出data传给上层,进入等待来自下层的调用 1状态;若收到的分组无比特差错不是期望接收的分组时,反馈ACK,并附加校验和;若收到的分组存在比特差错,反馈NAK,并附加校验和。

rdt2.1的特点:

  1. 接收方对其反馈的ACK或NAK信息添加了校验和。
  2. 发送方对其发送的分组添加了序号。

rdt2.2:基于rdt2.1的无NAK协议

rdt2.1中,收到非期望的分组时,会反馈ACK;收到发生比特差错的,会反馈NAK。

rdt2.2与rdt2.1的细微变化在于,不再反馈NAK。而是在收到非期望分组或者收到的分组发生比特差错时,均对上次正确接收的分组反馈一个ACK。

比如期望收到分组0,发来的是0且0没发生比特差错,则反馈ACK 0;期望收到分组1,发来的却是0,则无论0发没发生比特差错,均反馈ACK 0

下两图显示了rdt2.2发送方和接收方的有限状态机。

rdt3.0:底层信道不仅会错还会丢包

当发生丢包时,有可能是发送方发过去的数据丢失了,接收方收不到数据,所以无法给发送方反馈信息;也有可能是接收方收到了数据,但是接收方的反馈信息发生了丢失。

这两种情况发送方都没办法收到接收方的反馈信息,也就没办法进行状态变迁。整个传输链”死掉了“。

解决方法是让发送方等待一个固定时间,如果过了这个等待时间,还没收到反馈,就判定发生丢包,进行重传。

总结

至此,我们已经实现了一个可靠数据传输协议,至少在功能上是这样的。但是它的性能是堪忧的,因为它还在使用落伍的停等协议!下次将介绍流水线可靠数据传输协议来改善rdt3.0的性能。

全部评论

相关推荐

不愿透露姓名的神秘牛友
11-27 10:48
点赞 评论 收藏
分享
10-12 19:08
666 C++
花开蝶自来_:技能:听动物叫,让雪豹闭嘴
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务