TCP协议之下的可靠数据传输协议的原理
Author:Zreal
摘要
总所周知,因为可靠数据传输的实现问题,不仅仅是在运输层上,也会在链路层以及应用层出现。这篇文章在一般场景下考虑可靠数据传输的问题。探究可靠数据传输的一些原理,这些原理也是TCP协议所采用的许多原理。探究可靠数据传输协议的模型如何解决一系列物理逻辑上的问题。
可靠数据传输协议
可靠数据传输框架为上层实体提供的服务抽象是:数据可以通过一条可靠的信道进行传输。借助可靠地信道,传输数据比特就不会受到损坏或丢失,而且所有数据都是按照其发送顺序进行交互。而实现这种服务抽象就是可靠数据传输协议的责任。
图1来自:https://huminxi.netlify.com/2018/03/18/%E5%8F%AF%E9%9D%A0%E6%95%B0%E6%8D%AE%E4%BC%A0%E8%BE%93%E6%9C%8D%E5%8A%A1/
如图1所示,左图是可靠数据传输协议给上层提供的服务,提供的是一个稳定的可靠的信道(Reliable channel)。右图是服务的实现,如图,在数据传输协议的下层协议可能是不可靠的,因此可靠数据传输协议的实现是利用一定的方法对传输过程进行包装,避免下层不可靠协议在传输过程中信息的缺失,为上层提供一个可靠的信道
可靠数据传输协议的构建
简介:
这里将介绍考虑越来越多一般情况的条件下,可靠数据传输协议应该如何构建才能向上层提供可靠的信道。 注:下面的所有模型中将仅考虑单向数据传输的情况,即数据传输是从发送端到接收端。
经完全可靠信道的可靠数据传输:rdt1.0
这是一种最简单的情况,及底层信道是完全可靠的。(即图1(b)中的Unreliable channel为可靠信道),称该协议为rdt1.0。
图2来自:https://www.jianshu.com/p/85cf6fb605b3
图2(a)中的FSM(Finite-State-Machine)定义了发送方的操作,图2(b)中的FSM定义了接收方的操作。当上层调用发送方的rdt_read(data)读取传输的数据。经由make_pkt(data)将数据进行切分,产生一个包含传输数据的分组。在接受端通过rdt_rcv从底层信道接受一个分组,经过extract(packet,data)操作,从底层分组中提取出一个数据,并将数据传送给高层。
在这个简单模型中,因为底层信道是可靠的,接收端不需要提供任何反馈信息给发送方(不担心出错)。
经具有比特差错信道的可靠数据传输:rdt2.0
底层信道更为实际的模型是分组中的比特位可能受损的模型(比特因物理器件故障发生转置造成信息传输错误)。眼下假定所有发送的分组都按其发送顺序被接受。
现在有这样一个例子可以解释这个模型,如果一个人在给另一个人的通话中,说了一长串的信息,说完后如果另一个人完全接受,回复收到,继续通话。如果另一个人没有接受,回复没收到,则第一个人重新传达一边信息。这种方式使用了肯定确认和否定确认来保证信息可以安全传输。这种方式使得接收方让发送方知道哪些内容被正确接受,那些内容接受有误并因此需要重复。
在计算机网络中,基于这样的重传机制的可靠数据传输协议称为自动重传请求协议(ARQ Automatic Repeat reQuest)。
步骤:
-
差错检测:
用一种机制检测何时出现比特差错
-
接收方反馈:
“肯定确认” ACK,“否定确认”NAK,ACK与NAK作为一个分组进行反馈
-
重传
接收方收到差错分组时,发送方进行重传特定分组的信息
图三来自:https://zhuanlan.zhihu.com/p/36030269
在发送端:
rdt_send(data)
接受来自高层的数据make_pkt(data,checksum)
将数据分组并将用于差错检测的校验和信息写入分组首部udt_send(sndpkt)
将分组发送到信道rdt_rcv(rcvpkt)
接收从接收方发来的反馈信息。若为肯定确认isACK(rcvpkt)
,则本次传输完成,继续等待上层的调用;若为否定确认isNAK(rcvpkt)
,则说明数据出错,因此要重传数据,然后等待反馈结果,即重复本步骤。在接收端
rdt_rcv(rcvpkt)
接收分组,然后判断数据是否损坏
corrupt(rcvpkt)
若数据损坏,则通过make_pkt(NAK)
生成否定的反馈信息sndpkt
uncorrupt(rcvpkt)
若数据未损坏,则extract(packet ,data)
从分组中取出数据,然后deliver_data(data)
将数据上传给较高层。并则通过make_pkt(ACK)
生成肯定的反馈信息sndpkt
发送反馈信息
udt_send(sndpkt)
——引用自https://zhuanlan.zhihu.com/p/36030269
图三中,发送者有两个状态,一个是等待上层调用,发送分组。另一个是等待接收方的检验信息。注意:当发送者处于等待ACK或NAK的状态时不能接受上层调用(及传送新的数据),只有等到了ACK,才能继续传送下一个DATA。这种协议成为停等协议。
看似整个模型已经可以进行可靠的数据传输了,但是此模型成功运行的前提假设是 ACK和NCK的数据不会产生丢失。
对于这种情况有以下三种解决方案:
- 发送方再传一个ACK或NCK确定是否接受成功。显然这种方案实不可取的,这样走上了一个循环。
- 增加足够的校验位来减小ACK和NCK传输过程中的差错。
- 当发送方收到信息缺失的ACK或NAK分组时,重传当前数据分组即可
对于方法3,也会出现问题,如果重传一个一样的分组,会造成分组冗余(可能接收到两个一模一样的分组)。解决这个问题的一个简单的方法就是在数据分组中增加一个字段,让发送方对所有分组进行编号(0,1,0,1交替编号)。接收方只需要确定编号即可确定分组是否进行了重传。
发送方FSM描述图:
图三来自:https://zhuanlan.zhihu.com/p/36030269
发送方过程:
- 收到上层调用,编号分组0,发送
- 收到NAK或非完整,重发;收到ACK,下一步
- 收到上层调用,编号分组1,发送
- 收到NAK或非完整,重发;收到ACK,步骤1循环
接收方过程未太大变化,接受的分组必须按照0101序号排列,否则发送ACK;
上述模型及在 rdt2.0模型上进行了修改的rdt.2.1模型
经具有比特差错的丢包信道的可靠数据传输:rdt3.0(比特交替协议)
rdt3.0模型是为了面对底层信道可能会发生分组丢失的问题(这里所指是整个分组丢失,即package发送了,但接收方没收到,在现实生活中这是一个常见的问题)。下面待解决的有两个问题1.如何检测分组丢失。2.如何重传丢失分组。
检测分组丢失:为发送方设置一个定时器,设置一个时间阈值,每当发送一个分组时,就启动定时器,超过阈值,则重传分组。对于接收方,只需要对于序号进行冗余处理即可。
信息传输过程如图:
图4来自http://www2.ic.uff.br/~michael/kr1999/3-transport/3_040-principles_rdt.htm
图4 (a) 是在无丢包操作下的模型,此时没有超时出现,整个模型与rdt2.1无异。
图4(b)是在发送方出现分组丢失,发送方在限定时间内未收到ACK,超时,重新发送分组
图4(c)出现ACK丢失,和(b)情况相同,限定时间内未收到ACK,超时,重新发送分组
图4(d)出现过早超时,此时是时间阈值设置过小或发送ACK物理层中出现异常而产生的情况。此时接受方会收到两个相同的分组,进行冗余检测后,继续信息传递。
这样用rdt3.0 模型就解决了分组丢失问题
流水线可靠数据传输协议
从rdt1.0到rdt3.0,已经实现了一个具有比特差错和丢包信道的可靠数据传输协议。但是这种传输协议是一种停等协议,也就是必须成功接收到接收方的反馈信息,才能传递下一个分组。这样的传输效率是值得商榷的,每次传输都要等待一个往返延迟。如何解决传输效率的问题,放弃停等协议,成了下个协议模型的重点。
流水线可靠数据传输协议支持发送端可以同时发送多个分组到信道中,而无需等待收到对应ACK后发送下一个分组。这样传输附带以下协议上的改变:
- 增加序号的范围,之前比特交替的分组编号不在适用,当一次发送的分组多于两个时,分组编号会出现重复
- 建立缓冲区,缓存多个分组。因为多个分组接受过程中可能会失掉顺序,未收到特定分组也可能会要求重传分组,所以发送方需缓存已发送但未确认(未收到对应ACK的分组)分组,接收方需缓存失序的分组。
- 提高了性能,还需重新建立处理丢失,损坏及延时分组的方法。下面有两种方法:
回退N步法(Go-Back-N)或滑动窗口协议
图5 来源:https://hk.saowen.com/a/92a4fc910d3feb9609f4dd85d4b1a1c0e1fe3dcd413a12172f88adc4d30e1d3a
根据图5,首先定义了一个长度为N的窗口,在该窗口下,都是可以发送的分组或是发送了的分组。在窗口左边,是窗口已经发送且确认过的分组,窗口的右端不可发送的分组。当base指向的分组,发送且收到相应的ack后,窗口像右滑动一个分组,新增加的分组获得被发送的资格。 对于窗口内的每一个分组都被赋予了0——N-1的编号。整个调用过程如下:
发送方:
-
上层调用,检查窗口是否已满:
-
如果窗口已满,等待
-
窗口未满,产生一个分组发送到信道,递增nextseqnum。若只有该分组未被确认打开定时器。
-
- 收到发送方反馈ACK,若ACK受损,等待反馈,若收到序号为K的ACK,接收方采用累计确认,表示前K个分组已经被成功接收。把Base置为K+1,如果此时Base==nextseqnum,整个分组已经被确认。关闭计时器。若还有分组未被确认则重启定时器,整个过程只有一个计时器,它记录的是最早发送但未被确认的分组的时间。
- 如果超时,重新发送所有已经发送但未被确认的分组。
1
2
接收方:
采用累计确认,数据按序交付。比如它期待接受序号为k的分组,而序号为k+1的分组却先到达了,此时接收方会丢弃该分组(或者缓存),并发送ACK 序号为k。
####选择重传
发送方只会重传那些怀疑在接受方出错的分组从而避免不必要的重传。
图六来自https://zhuanlan.zhihu.com/p/36030269
图6(a)是发送方的窗口,图6(b)是接收方的窗口
选择重传与GBN最大的区别是N个分组,每个分组都有自己的计时器,当某个分组超时时,可以重传具体的某个分组,而不用一次性重传所有的未被确认的分组,减少了重传的次数。
总结
从最初的差错检验,到重传确认,再到累计确认,最后解决传输效率问题,可靠数据传输协议一次次迭代,用上述原理解决了一个个物理上效率上的问题。最终形成了基本的可靠数据传输协议。其中用到了某些原理,也是TCP协议所依赖的。了解清楚这些模型的更迭以及解决方案所以来的原理,更有利于了解TCP协议对于解决一般数据传输情况的解决方案。