可靠数据传输原理

引言

众所周知,TCP协议是一个面向连接,可靠的传输层的协议,而IP协议是不可靠的网络层协议,IP协议能做到的就是“尽力而为的”交付分组,即不能确保分组一定能从发送方到达接受方,那么TCP协议是如何在不能信赖的网络层上建立可靠的连接确保对方能准确无误的收到分组呢?

在深入了解TCP协议之前,需要先熟悉一些基本的可靠数据传输原理,本文是笔者在阅读《计算机网络:自顶向下方法》中的传输层后做下的笔记总结。注意,TCP协议只是以这些原理为基础,实际还会在这些原理中做出一些改进。

准备

在了解可靠数据传输原理之前,相信大家都会有这样的一个疑问,为什么需要可靠数据传输原理呢?难道底层的信道不能直接做到准确的数据传送么?在数据传送过程中数据会发生哪些损坏?
笔者首先列出,当数据在传送过程中会发生哪些损坏:

  • 分组的数据(比特)在传输过程中发生了损坏,即部分比特从0变成了1或者从1变成了0。
  • 分组在传送过程中丢失了,比如当分组到达一个中转的路由器时,路由器的输入队列已经满了,此时分组就会溢出,即被丢弃。
  • 分组没有按照发送的顺序到达接收方。

因此,可靠数据传输原理就是为了解决上述的问题的。
现在,假设将发送方和接受方分为三层模型,其中,分组通过底层信道传输,底层信道不会对分组进行重新排序,发送方模型如下:

Created with Raphaël 2.1.0 上层调用 传输层 底层信道

第一种情况:底层信道完全可信

现在假设底层信道完全可信,即上面笔者所说的三种分组损坏情况都不会发生,那么分组只要直接从发送方通过底层信道送往接收方,接收方也不需要给发送方回应,因为分组准确地到达并且不会发生任何差错。

第二种情况:在底层信道中分组可能会出现比特差错

分组的数据在传输中出现比特差错是非常常见的,因此,发送方需要一个来自接收方的确认信息来告诉发送方传送的分组数据是否有差错,那么,当数据无误的时候,发送方发送一个ACK(肯定确认)的回应表示数据没有问题,而如果数据错误,则发送一个NAK(否定确认)的回应表示数据有问题需要重传。其中,发送方在发送分组的时候需要为分组计算一个检验和,当分组到达以后,接收方用同样的计算方法对分组进行计算,将得到的检验和与发送方的检验和进行核对以检验分组数据是否传输出错。

情况流程如下:

  • 上层通知发送方发送分组数据,发送方将分组数据计算以后得到检验和并放置在头部之中,在发送完分组以后,发送方处于等待接受方回应的状态中
  • 接收方收到了分组(注意,在该种情况下假设分组并不会中途丢失,因此接收方一定会接收到分组),检验数据以后:
    • 数据正确则返回ACK
    • 数据错误则返回NAK。
  • 发送方收到了接收方的反馈信息,此时有两种情况:
    • 接收到了NAK,表明数据有错,因此发送方重传该数据,并处于等待接收方回应的状态中
    • 接收到了ACK,表明数据正确,发送方继续等待上层的调用。

其中,在发送方与接收方的交互中,大家可以看到使用了差错检测,接收方反馈,重传这三个功能,基于这三个功能以及还有超时功能的可靠数据传输协议被称为ARQ协议。(维基百科传送门:ARQ)

笔者在上面加粗了发送方在发送完分组数据以后的状态,这是因为当发送方没有连续发送分组而是处于等待接收方回应的状态时,此时发送方与接收方交互的协议被称为停等协议。

如果接收方发送的ACK或者NAK分组出现了比特差错?

当发送方接收到来自接收方的反馈分组时,反馈分组出现了比特差错,发送方无法辨认反馈是ACK还是NAK,因此,一个简单的解决方法是,发送方只要重传当前分组就行了。但是,又有另一个问题发生了,接收方如何知道收到的分组是一个新的分组还是说是一个重传的分组呢?因此,还需要给分组准备一个简单的序列号(用一个比特位来表示,即0和1),使接收方能返回不同分组的回馈。

笔者重点描述一下如果接收方返回的回馈发生了比特差错的情况:

  • 上层通知发送方发送分组0,发送方在发送完分组0之后处于等待分组0的回馈的状态中
  • 分组0到达了接收方,此时有两种情况:
    • 分组0正确,接收方返回了ACK0,并处于等待分组1的状态中
    • 分组0不正确,接收方返回了NAK0,并处于等待分组0的状态中
  • (笔者在这里讨论第一种情况,当接收方返回了ACK0,但是ACK0受损的情况)
  • 发送方收到了分组0的回馈,但发现分组损坏了,此时发送方还处于等待分组0的状态中,所以重传当前分组0。
  • 接收方此时处于等待分组1的状态,但是却收到了分组0(冗余分组),因此接收方知道返回的ACK受到了损坏,所以重发ACK0,此时接收方仍然在等待分组1。
  • 发送方在确认回馈为ACK0无误的时候,才继续发送分组1。

在上述的流程中,大家可以注意到,无论接收方向发送方返回一个正确传达的NAK,还是返回一个破损的回馈分组,发送方都需要重传当前分组,因此,当接收方收到一个破损的分组,可以向发送方发送上一个已经正确接收的分组的ACK,表示需要重传接收方已经正确接收分组的下一个分组(即发送方的当前分组,不过在传送过程中破损了),这样就可以通过一个ACK来确认多种情况。

第三种情况:在底层信道中出现了分组丢失

假设现在分组有可能被发送方与接收方之间的中转路由器丢弃了,而此时发送方与接收方根本不知道分组被丢弃了,于是双方都陷入了一个死循坏的等待中。因此,一个简单的解决方法是,发送方需要设置一个定时器,当发送分组的时候开始计时,如果在一定时间内(传播时延+接收方处理分组时间)没有收到接收方的回馈分组,则重传当前分组。

需要注意的是,由于发送方使用了计时器,所以当发送方收到了当前分组的上一个分组的ACK的时候(即当前分组传输损坏)或者回馈分组损坏的时候(这两个操作都不会重置计时器),并不会立即发送当前分组,而是等到计时器超时才重新发送当前分组。

流水线可靠数据传输协议

在解决了上面三种分组数据损坏的问题后,得到的的确是一个可靠的数据传输协议,但是,大家应该注意到上述的可靠数据传输协议采用的是停等协议,这必然造成了效率的地下,不能对链路带宽充分的利用。因此,有没有方法使发送方不断的发送分组,而无需等待发送分组的确认呢?

那么,如果要实现流水线发送分组而无需确认分组,需要做到:

  • 增加分组的序号范围,不能简单的使用0和1来作为分组的序号(顺便一提在TCP中分组序号是按照数据的字节顺序给的,并非按照分组排序)。
  • 发送方与接收方都需要有缓存用来存储分组。
  • 提供当分组数据出错,超时,重传的处理方法:选择重传(SR)协议以及回退N步(GBN)协议。

回退N步协议(GBN)

假设在序号空间内,划分一个长度为N的子区间,这个区间内包含了已经被发送但未收到确认的分组的序号以及可以被立即发送的分组的序号,这个区间的长度就被称为窗口长度。(随着发送方方对ACK的接收,窗口不断的向前移动,并且窗口的大小是可变的,因此GBN协议也被称为滑动窗口协议)

笔者描述一下当上层调用发生时,发送方的窗口变化:

  • 当上层调用发生时,此时根据窗口内的分组情况作出不同的动作响应:
    • 如果窗口内已经被发送但未收到确认的分组数目已经达到了窗口长度(此时就没有可以被立即发送的分组的序号了),发送方可以把上层的数据缓存起来或者告诉上层隔一段时间以后再来调用。
    • 如果窗口内还有可以分配的立即被发送的分组序号,则为上层的数据分配分组序号并立即发送,更新可以被立即发送的分组序号。

那么,为什么要限制窗口长度为N,而不直接设置窗口长度为整个发送方的缓存长度呢?这是为了给TCP协议提供流量控制的功能(注意,流量控制与差错控制要区分)。

GBN协议还采取了累积确认,当发送方收到一个对分组n的ACK的时候,即表明接收方对于分组n以及分组n之前的分组全部都收到了。

对于超时的触发,GBN协议会将当前所有已发送但未被确认的分组重传,换句话说,如果当前窗口内都是已发送但未被确认的分组,一旦定时器发现窗口内的第一个分组超时,则窗口内所有分组都要被重传。每次当发送方收到一个ACK的时候,定时器都会被重置。

GBN协议对于接收方则相对比较简单,接收方只需要按序接收分组,对于比当前分组序号还要大的分组则直接丢弃。假设接收方正在等待接收分组n,而分组n+1却已经到达了,于是,分组n+1被直接丢弃,正是因为这种处理,所以发送方并不会出现在连续发送分组n,分组n+1之后,而分组n+1的ACK却比分组n的ACK更早到达发送方的情况。
那么,GBN协议对超前到达的分组直接丢弃的做法会不会有点过于浪费呢?大家可以注意一下GBN的超时机制,即使分组n+1已经预先到达了接收方,但只要分组n没有到达接收方,则很有可能导致发送方的定时器超时,而一旦定时器超时,则所有已发送但未被确认的分组都会被重传,其中就包括了分组n+1,因此,接收方只要简单的丢弃提前到达的分组n+1就可以了。

选择重传协议(SR)

在了解了GBN协议之后,或许有人对GBN的超时重传机制感到困惑,有必要因为一个分组的超时而重传所有的已发送而未被确认的分组么?SR协议允许接收方对于超前分组的到达返回ACK,则发送方就会出现在收到分组n的ACK之前接收到分组n+1的ACK的情况。

那么,对于SR协议来说,发送方需要做到:

  • 为每一个已发送但未被确认的分组都需要设置一个定时器,当定时器超时的时候只发送它对应的分组。
  • 当发送方收到ACK的时候,如果是窗口内的第一个分组,则窗口需要一直移动到已发送但未未确认的分组序号。

接收方需要做到:

  • 维护一个接收窗口(注意,在之前的协议之中,接收方都只需要维护当前分组的序号即可)。
  • 当分组序号在接收窗口之内的时候,返回该分组的ACK并视情况移动接收窗口,而如果分组序号在分组之前(冗余分组),返回该分组的ACK。

需要注意的是,接收窗口内不能出现两个分组的相同序号,否则接收方无法辨认需要对一个冗余分组的ACK还是对一个新的分组的ACK,因此,接受窗口长度必须小于或者等于序号空间的一半。

最后,笔者在查阅滑动窗口协议的时候发现了根据发送窗口与接收窗口的长度来区分停等协议,GBN协议,SR协议的方法:滑动窗口协议 。

你可能感兴趣的:(网络)