可靠数据传输服务

我们知道,传输层有两个协议,分别是 UDP 和 TCP。其中 UDP 提供的是无连接的、不可靠的数据传输服务。而 TCP 则提供面向连接、带拥塞控制的可靠数据传输服务。今天,我们就来聊一聊怎么实现可靠数据传输服务(reliable data transmission service)。

我们的目标

先来看一张图。

图 3-8 可靠数据传输服务的模型与实现

名词解释:

  • Application Layer 应用层
  • Transport Layer 传输层
  • Network Layer 网络层
  • Sending process 发送进程
  • Receiver process 接收进程
  • Reliable channel 可靠信道
  • Unreliable channel 不可靠信道
  • Data 数据
  • Packet 分组
  • rdt reliable data transfer 可靠数据传输
  • udt unreliable data transfer 不可靠数据传输

图 3-8a 描述的是可靠数据传输框架的模型与实现。图中,为上层实体提供的服务抽象是:数据可以通过一条可靠的信道进行传输。借助于可靠信道,传输数据比特就不会受到损坏(由 0 变为 1,或者由 1 变为 0)或丢失,而且所有数据都是按照其发送顺序进行交付。这就是 TCP 向调用它的因特网应用所提供的服务模型。

图 3-8b 说明了用于数据传输协议的接口。发送方通过调用 rdt_send 函数,可以将数据交付给接收方的较高层(这里 rdt 表示可靠数据传输协议,_send 指示 rdt 的发送端正在被调用)。当分组到达接收端时,将调用 rdt_rcv,而 rdt 协议向较高层交付数据,则是通过调用 deliver_data 来完成的。

怎么实现

我们现在一步一步地研究一系列协议,它们一个比一个复杂,最后得到一个无错、可靠的数据传输协议。

rdt 1.0

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

有限状态机(finite-state-machine)

首先,我们考虑最简单的情况,即底层信道是完全可靠的。我们称该协议为 rdt1.0,该协议本身是简单的。图 3-9 显示了 rdt1.0 发送方和接收方的有限状态机(Finite-State-Machine, FSM)的定义。

图 3-9 rdt1.0 用于完全可靠信道的协议

图 3-9a 中的 FSM 定义了发送方的操作,图 3-9b 中的 FSM 定义了接收方的操作:

  • 发送方和接收方有各自的 FSM。
    • 图中发送方和接收方的 FSM 每个都只有一个状态。
  • FSM 中的箭头指示了协议从一个状态变迁到另一个状态。
    • 因为图中的每个 FSM 都只有一个状态,所以变迁是从一个状态返回到自身。
  • 引起变迁的事件显示在表示变迁的横线上方,事件发生时所采取的动作显示在横线下方。
    • 如果对一个事件没有动作,或没有事件就发生了动作,我们将在横线下方或上方使用符号 𐌡,以明确地表示缺少动作或事件。
  • FSM 的初始状态用虚线表示。
    • 尽管图中的 FSM 只有一个状态,但马上我们就将看到多状态的 FSM,因此标识每个 FSM 的初始状态是非常重要的。

发送端

rdt 的发送端只通过 rdt_send 事件接受来自较高层的数据,产生一个包含该数据的分组(经由 make_pkt 动作),并将分组发送到信道中。rdt_send 事件是由较高层应用的过程调用产生的。

接收端

在接收端,rdt 通过 rdt_rcv 事件从底层信道接收一个分组,从分组中取出数据(经由 extract 动作),并将数据上传给较高层(通过 deliver_data 动作)。rdt_rcv 事件是由较低层协议的过程调用产生的。

小结
在这个简单的协议中,一个 data 和一个 packet 在内容上没有任何差别。而且,所有分组是从发送方流向接收方。有了完全可靠的信道,接收端就不需要提供任何反馈信息给发送方,因为不必担心出现差错!同时我们也已经假定了接收方接收数据的速率能够与发送方发送数据的速率一样。因此,接收方没有必要请求发送方慢一点!

rdt 2.0

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

停等协议(stop-and-wait)

底层信道更为实际的模型是分组中的比特可能受损。在分组的传输、传播或缓存的过程中,这种比特差错通常会出现在网络的物理部件中。我们眼下继续假定所有发送的分组(比特可能受损)将按其发送的顺序被接收。

在开始研究能通过这种信道进行可靠通信的协议之前,首先考虑一下人们会怎样处理这类情况。回想一下你自己是怎样通过电话口述一条长消息的(如电话号码)。在通常情况下,报文接收者在听到、理解并记下每句话后可能会说『OK』。如果报文接收者听到一句含糊不清的话时,他可能要求你重复刚才那句话。这种口述报文协议使用了肯定确认(positive acknowledgement)『OK』与否定确认(negative acknowledgment)『请重复一遍』。这些控制报文使得接收方可以让发送方知道哪些内容被正确接收,哪些内容接收有误并需要重复。在计算机网络环境中,基于这样重传机制的可靠数据传输协议称为自动重传请求(Automatic Repeat reQuest, ARQ)协议。

基本上,ARQ 协议中需要包括下列三种功能来处理存在比特差错的情况:

  • 差错检测。
    • 首先,需要一种机制使接收方可以检测到何时出现了比特差错。
    • 实现这个机制的技术要求有额外的比特(非原始数据中的比特)从发送方发送到接收方。这些比特将存在于 rdt2.0 数据分组的『分组检验和』字段中。
  • 接收方反馈。
    • 因为发送方和接收方通常在不同端系统上执行,可能相隔数千英里,发送方了解接收方情况(例如,分组是否被正确接收)的唯一途径就是让接收方提供明确的反馈信息给发送方。
    • 在口述报文情况下回答的『肯定确认』(ACK)和『否定确认』(NAK)就是这种反馈的例子。类似地,我们的 rdt2.0 协议将从接收方向发送方回送 ACK 与 NAK 分组。理论上,这些分组只需要一个比特长,如用 0 表示 NAK,用 1 表示 ACK。
  • 重传。
    • 接收方收到有差错的分组时,发送方将重传该分组。

图 3-10 说明了表示 rdt2.0 的 FSM,该数据传输协议采用了差错检测、肯定确认与否定确认

图 3-10 rdt2.0 用于具有比特差错信道的协议

注意:当发送方处于等待 ACK 或 NAK 的状态时,它不能从上层获得更多的数据。也就是说,rdt_send 事件不可能出现,仅当接收到 ACK 并离开该状态时才能发生这样的事件。因此,发送方将不会发送新数据除非发送方确信接收方已正确接收当前分组。由于这种行为,rdt2.0 这样的协议被称为停等(stop-and-wait)协议。

ACK/NAK 受损

rdt2.0 协议看起来似乎可以运行了,但遗憾的是,它存在一个致命的缺陷。我们没有考虑到 ACK 或 NAK 分组受损的可能性!(在继续研究之前,你应该考虑怎样解决该问题)至少,我们需要在 ACK/NAK 分组中添加检验和比特以检测这样的差错。更难的问题是协议应该怎样纠正这样的差错。这里的难点在于,如果一个 ACK/NAK 分组受损,发送方就无法知道接收方是否正确接收了上一块发送的数据。

我们考虑处理受损 ACK 和 NAK 的 3 种可能情况:

  • 第一种,在口述报文情况下人们通常的做法。
    • 如果说话者不理解来自接收方回答的『OK』或『请重复一遍』,说话者将可能问 『你说什么?』(这样就在我们的协议中引入了一种新的发送方到接收方的分组类型),接收方则将复述其回答。
    • 但是如果说话者的『你说什么?』产生了差错,情况又会怎样呢?由于接收者不明白那句混淆的话是口述内容的一部分还是一个要求重复上次回答的请求,很可能也回一句『你说什么?』。于是越回答越乱!
  • 第二种,增加足够长度的检验和比特。
    • 使收到受损 ACK/NAK 的发送方不仅可以检测出差错,还可恢复差错。对于会产生差错但不丢失分组的信道,这就可以直接解决问题。
  • 第三种,当发送方收到含糊不清的 ACK/NAK 分组时,只需重传当前数据分组即可。
    • 然而,这种方法在发送方到接收方的信道中引人了冗余分组(duplicate packet)。
    • 冗余分组的根本困难在于接收方不知道它上次所发送的 ACK/NAK 是否被发送方正确地收到,这就导致它无法确认这一次接收到的分组是新的还是一次重传!

解决这个问题的一个简单方法(几乎所有现有的数据传输协议都采用了这种方法,例如 TCP)是在数据分组中添加一个字段,让发送方对其数据分组进行编号,将数据分组的序号(sequence number)放在该字段。于是,接收方只需要检查序号即可确定收到的分组是否来自重传。对于停等协议这种简单情况,1 比特序号就足够了,因为它可让接收方知道发送方是否正在重传前一个发送分组(接收到的分组序号与最近收到的分组序号相同),或是一个新分组(序号变化了)。因为目前我们假定信道不丢失分组,ACK 和 NAK 分组就不需要指明它们要确认的分组序号。发送方知道所接收到的 ACK 和 NAK 分组(无论是否是含糊不清的)是为响应其最近发送的数据分组而生成的。

图 3-11 和图 3-12 给出了对 rdt2.1 的 FSM 描述,这是 rdt2.0 的修订版。rdt2.1 的发送方和接收方 FSM 的状态数都是以前的两倍。这是因为协议状态此时必须反映出目前(由发送方)正发送的分组或(在接收方)希望接收的分组的序号是 0 还是 1。值得注意的是,发送或期望接收 0 号分组的状态中的动作与发送或期望接收 1 号分组的状态中的动作是相似的;唯一的不同是序号处理的方法不同。

图 3-11 rdt2.1 发送方

图 3-12 rdt2.1 接收方

去掉 NAK

协议 rdt2.1 使用了从接收方到发送方的肯定确认和否定确认。当接收到失序的分组时,接收方对所接收的分组发送一个肯定确认。如果收到受损的分组,则接收方将发送一个否定确认。如果不发送 NAK,而是对上次正确接收的分组发送一个 ACK,我们也能实现与 NAK 一样的效果。发送方接收到对同一个分组的两个 ACK (即接收冗余 ACK)后,就知道接收方没有正确接收到跟在被确认两次的分组后面的分组。

rdt2.2 是在有比特差错信道上实现的一个无 NAK 的可靠数据传输协议,如图 3-13 和图 3-14 所示。rdt2.1 和 rdt2.2 之间的细微变化在于,接收方此时必须包括由一个 ACK 报文所确认的分组序号(这可以通过在接收方 FSM 中,在 make_pkt 中包括参数 ACK 0 或 ACK 1 来实现),发送方此时必须检查接收到的 ACK 报文中被确认的分组序号(这可通过在发送方 FSM 中, 在 isACK 中包括参数 0 或 1 来实现)。

图 3-13 rdt2.2 发送方

图 3-14 rdt2.2 接收方

rdt 3.0

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

丢包检测和恢复

现在假定除了比特受损外,底层信道还会丢包,这在今天的计算机网络(因特网)中很常见。协议现在必须解决另外两个关注的问题:怎样检测丢包以及发生丢包后该做些什么。在 rdt2.2 中已经研发的技术,如使用检验和、序号、ACK 分组和重传等,使我们能给出后一个问题的答案。为解决第一个问题,还需增加一种新的协议机制。

存在很多方法用于解决丢包问题,在这里,我们让发送方负责检测和恢复丢包工作。假定发送方传输一个数据分组,该分组本身或者接收方对该分组的 ACK 发生了丢失。在这两种情况下,发送方都收不到来自接收方的响应。如果发送方愿意等待足够长的时间以便确定分组已经丢失,则它只需重传该数据分组即可。

但是发送方需要等待多久才能确定已丢失了某些东西呢?很明显发送方至少需要等待这样长的一个时间:即发送方与接收方之间的一个往返时延(可能会包括在中间路由器的缓冲时延)加上接收方处理一个分组所需的时间。在很多网络中,最坏情况下的最大时延是很难估算的,确定的因素非常少。此外,理想的协议应该能尽快地从丢包中恢复出来。等待一个最坏情况的时延可能意味着要等待一段较长的时间,直到启动差错恢复为止。因此实践中采取的方法是发送方『明智』地选择一个时间值,以判定可能发生了丢包(尽管不能确保)。如果在这个时间内没有收到 ACK,则重传该分组(注意到如果一个分组经历了一个特别大的时延,发送方可能会重传该分组,即使该数据分组及其 ACK 都没有丢失)。这就在发送方到接收方的信道中引入了冗余数据分组(duplicate data packet)的可能性。幸运的是,rdt2.2 协议已经有足够的功能(即序号)来处理冗余分组的情况。

定时重传

从发送方的观点来看,重传是一种万能灵药(重启也是)。发送方不知道是一个数据分组丢失,还是一个 ACK 丢失,或者只是该分组或 ACK 过度延时。在所有这些情况下,动作是同样的:重传。为了实现基于时间的重传机制,需要一个倒计时器(countdown timer),可中断发送方。因此,发送方需要能做到:

  • 每次发送一个分组(包括新分组和重传分组)时,便启动一个定时器。
  • 响应定时器中断(采取适当的动作)。
  • 终止定时器。

图 3-15 给出了 rdt3.0 的发送方 FSM,这是一个在可能出错和丢包的信道上可靠传输数据的协议。

图 3-15 rdt3.0 发送方

rdt3.0 接收方

因为它和 rdt2.2 一样,没有 ACK 和 NCK 的区别,而且传输的数据分组也标了序号(0 和 1),所以它不受超时重传的影响。因此 rtd3.0 和 rtd2.2 的接受方是一样的 FSM 图。

图 3-16 rdt3.0 接收方

现在我们归纳一下数据传输协议的要点。在检验和、序号、定时器、肯定和否定确认分组这些技术中,每种机制都在协议的运行中起到了必不可少的作用。至此,我们得到了一个可靠的数据传输协议!

rdt3.0 的性能问题

rdt3.0 是一个功能正确的协议,但并非人人都对它的性能满意,特别是在今天的高速网络中更是如此。rdt3.0 性能问题的核心在于它是一个停等协议!

预知后事如何,且听 下回分解

引用