四、可靠数据传输协议
可靠数据传输原理
- 可靠:不错、不丢、不乱
- 信道的不可靠特性决定了可靠数据传输协议(rdt)的复杂性
基本结构(接口)
- rdt_send将上层数据交给rdt协议,将事情交给传输层。单向箭头表示上层网络应用单向调用传输层协议,只调用一次(交给TCP了)。
- udt_send()被rdt调用,不可靠信道指的是网络层地IP协议。双向箭头
- rdt_rcv:当数据包到达接收方信道时被调用。会触发接收方的rdt协议。双向箭头。
- deliver_data:交付数据。并且是可靠的交付数据。也是单项的,意味着可靠数据传输协议将所有的事情做好以后才把数据正确的交付给上层。
传输协议的刻画
- 利用状态机刻画。
RDT1.0
- 底层信道完全可靠。
- 故发送方和接收方相互独立,无耦合关系。
- 发送方:当上层调用产生事件rdt_send时,data为欲交付的数据。采取活动:创建分组packet,调用udt_send发送数据。
- 接收方:等待下层的调用。从分组中将数据提取,由于信道完全可靠不需要检测数据是否出现异常,因此直接向上层deliver即可。
Rdt2.0
- 研究信道对象:底层信道可能翻转分组中的位(bit);
- 利用校验和检测位错误
- 如何恢复:需要引入新的消息。
- ACK机制:接收方显式告诉发送方分组已经正确接收。
- NAK:错误接收。
- 发送方收到NAK后重传即可
- 称为ARQ协议。差错检测(包括但不限于校验和),接收方反馈控制消息。(ACK,NAK)。重传。
描述:
- 发送方需要两个状态,称为停等协议
- 发送方处于左侧状态时,如果收到上层应用层的调用,于是协议打包数据,加上校验和,然后调用udt_send发送数据。进入右边的状态。
- 当发送端处于右侧状态时,如果收到isNAK,则重发,还是处于该状态。
- 否则如果收到ACK,重新回到左侧的状态。
- 接收方仅单个状态,当rdt_rcv被调用时检查校验位。如果检查正确,那么提取数据,向上层交付,并且告诉发送方ACK。否则发送NAK。
Rdt2.1和Rdt2.2
- 如果ACK/NAK消息发生错误/被破坏(corrupted)会怎么样?
- 如果坏掉,则重传,但直接重传会造成重复分组问题。
- 方案:发送方为每个分组增加序列号。接收方丢弃重复分组。
Rdt 2.1发送方
- 在状态1,上层数据加上校验和和序列号0打包并发送,转移到状态2
- 在状态2,如果接收到的数据(ACK或NAK)被破坏那么重新发送数据。停留在状态2.(请求接收方再说一遍,重新发送一次序列号为0的数据)
- 在状态2,如果数据没有被破坏并且收到的ACK,那么转移到状态3.
- 在状态3,发送序列号为1的数据(加上校验和等信息),转移到状态4;
- 状态4和状态2形式上对称
Rdt2.1接收方
- 处于状态1,期望得到序列号为0的数据
- 处于状态2,期望得到序列号为1的数据
- 在状态1如果收到的数据校验和异常,那么发送NAK
- 在状态1如果收到序列号为1的数据(与期望不一致),那么重新发送一次ACK给发送方,但并不向应用层交付数据。(避免了重复分组)
- 在状态1如果收到序列号为0的数据(与期望得到的一致),那么发送ACK转而状态2期望得到序列号为1的数据,同时需要向应用层交付得到的数据。
对比2.1和2.0
Rdi 2.2 无NAK消息协议
- 在ACK消息中显式的加入最后一个被正确接收的分组的序列号。
- 发送方收到重复ACK之后,采取与收到NAK消息相同的动作
- 注意在接收方收到错误的数据或该数据不是期望的数据(序列号为0)时,均利用刚刚从状态1到状态0转换时打包好的(ACK1和checksum)告知发送方重新发送期望的数据(序列号为0的数据)
RDT3.0-等待合理的时间
工作机制
- 如果信道可能发生分组的丢失该如何处理呢?
- 如果发送方分组丢失,如果采用上述协议,发送方不会收到接收方任何发过来的信息。就会一直处于某个状态中无线等待。
- 解决方案:发送方等到合理长度的时间。
- 如果没收到则重传
- 如果分组或ACK只是延迟而不是丢失,组序列号能有效解决重复发送的问题。
- 需要一个定时器
- 如图所示,在应用层请求协议传输数据后,协议发送数据,计时器启动。转到期望ACK0的状态
- 如果处在ACK0且计时器超时(分组可能丢失),那么重新发送分组。计时器复位。
- 这里有个比较subtle的地方,看到接收到的ACK为1(即对应NAK)或ACK被破坏时,不做任何操作。而是等待计时器超时再重新发送;
典型场景:
- 无丢包情况
- 丢包情况:等待到计时器超时重新传递分组;
![在这里插入图片描述](https://img-blog.csdnimg.cn/29a86965003446d58ac5f - ACK丢失情况:pk1到达了receiver,发送ACK1,但丢失。重传PK1,即接受方收到了两次。以序列号机制处理(不上载数据)重复分组。
- ACK1没有丢失,但由于定时器再收到ACK1之前就超时了。(能够处理重复分组)。发完第二个pk0处于如图所示的状态。这里重复的ACK1的处理方式:参照状态机是直接忽略?忽略后能够正常处理ACK0。
性能分析
流水线机制
- 为每个输送中的分组添加一个唯一的序号
- 发送方和接收方两段也许不得不缓存多个分组
滑动窗口协议-GBN(后退N帧)
-
协议的模拟网站:GBN模拟 -
分组头部包含k-bit序列号 -
窗口尺寸为N,最多允许N个分组未确认。
- 意思是流水线中的未确认分组数不能超过某个最大允许数量N
-
绿色表示已经确认的序列号 -
黄色表示已经发送但还未被确认的序列号;send_base表示最小的这类序列号。 -
蓝色表示还没有使用的序列号。nextseqnum指向最小的这类序列号 -
定义
A
C
K
(
n
)
:
ACK(n) :
ACK(n): 确认到序列号n(包含n)的分组均已被正确接收
- 表示序列号
n
,
n
?
1
,
n
?
2
?
1
n,n-1,n-2\cdots 1
n,n?1,n?2?1的所有分组都被成功接收。
-
假定第n个分组发生了超时事件(超时Timeout(n)事件): 重传序列号大于等于n,还未收到ACK的所有分组
发送方(扩展的有穷状态自动机)
-
timer应该是一个全局计时器。发送方仅有的一个计时器。 -
注意base指向第一个还没有确认的分组,seqnum指向第一个空闲序列号 -
初始时base == 1,nextseqnum = 1 -
如果nextseqnum<base+N,则意味着窗口还没有用完(还有可以使用的nextseqnum)。还可以接着发送分组。如果base和nextseqnum相同,则启动定时器。
- 如果窗口已经用完了,则直接refuse掉欲新发送的数据
-
Timeout事件
- 首先重启计时器
- 将所有已发送但还没有确认的分组重新发送一遍(即[base,seqnum-1]的所有分组)
-
收到接收消息:
- 令base指向最后已经确认的序列号(即返回的序列号)的下一个序列号(即下一个还未被确认的序列号) ;
- 因为这意味着最后确认的序列号及其前面的序列号都进行了确认。
- 这意味着窗口进行了滑动。(窗口固定大小N,向前滑动了一个单位,注意上面的状态的if判断条件(nextseqnum<base+N)
- 关注这里计时器的行为:
- if条件为真:表明没有已经发送但是未被确认的分组,停止计时器。
- if条件为加:表明仍有已发送但是还未被确认的分组,定时器被重新启动
- 而在第一步(窗口没用完发送数据时)start_timer开始启动而且仅在窗口中没有任何东西时启动的(也就是发送第一个分组时)。这时这个第一个分组还未被接收。因此此时start_timer可以看作针对于这个分组的计时器。
- 一旦收到ACK即接收消息时,计时器重新启动且此时base会指向ACK组序号加1的分组(即最早已发送,但未确认的分组)。
- 注意上述对于Timer重启的条件的总结。可以得出这里的全局计时器可看作是:最早的已发送但未确认的分组所使用的计时器
GBN接收方
-
ACK机制: 发送拥有最高序列号的、已被正确接收的分组的ACK;维护变量expectedseqnum当前期望收到的序列号。 -
如右上角所示,如果当前收到了一个分组并且没坏,并且这个分组是当前所期望的:那么上载数据。并令期望收到的分组序列号+1; 左侧的代码为初始化 -
丢弃接收方没有缓存,重新确认序列号最大的、按序到达的分组 -
默认操作为(例如收到损坏或)发送当前确认的最大的序列号。
GBN实例
- 假定窗口大小为4
- 发送方一直发送直到pk3,此时窗口已满
- pk0和pk1都已成功的到达了对方分别发送ACK0和ACK1,pk2丢失了。
- pk3到达后被丢失。因为现在期望的是2
- 发送方收到ACK0和ACK1后可以发pk4和pk5。收到ACK1后base被指向2,计时器重启。这时的计时器应该是针对于分组2的计时器;
- 这时分组2的计时器发生超时,于是从base开始发直到最后一个未确认的分组seqnum - 1
w
滑动g窗口协议-SR协议(selective-repeat)
- 模拟:SR协议模拟
- 设窗口长度N为流水线中未完成,未被确认的分组数。
- 通过让发送方仅重传那些它怀疑在接收方出错(丢失或受损)的分组而避免不必要的重传;
发送方动作
- 从上层收到数据:
- 检查下一个可用于该分组的序号。位于窗口内则将数据打包并发送(和GBN一样),否则refuse
- 超时:
- 每个分组都拥有自己的定时器。使得超时发生后只发送一个分组。
- 收到ACK:
- 如果序号在窗口内,将那个被接收方确认的分组标记为已经接受
- 如果该分组序号为send_base,则此指针向前移动到具有最小序号的未确认分组处
接收方动作:
- 正确收到窗口内即[recv_base,recv_base + N - 1]的分组。
- 发送该分组的ACK
- 如果该分组以前未收到过则缓存该分组;
- 如果该分组的序号等于接收窗口的基序号,则该分组及其之后连续的分组(已经缓存的)交付给上层。窗口移动到下一个没有被接收到的分组。例如上图中如果此时rcv_base处的分组被收到,那么rcv_base向右移动到下一个灰色的方块处。
- 正确收到[rev_base - N,rev_base - 1]的分组:
- 这可能是由于ACK在传输过程中造成了损坏或丢失。
- 这种情况下需要产生ACK给发送方
- 为什么是N?笔者有点Intution但还不能给出严格证明
- 这一步是很重要的,必要性由下面的额图给出:
- 接收方正确收到数据并发送ACK(黄色块)。按上述算法接收方窗口向前滑动
- 把那些黄色的ACK全部kill掉。
- 等待超时后,发送方会重新发送分组
- 假设此时ACK正确被接收,那么发送方窗口会向前移动
SR协议的实例
|