Java网络编程之可靠数据传输原理

本篇介绍计算机网络中的可靠数据传输原理,部分内容总结摘抄自《计算机网络:自顶向下方法》,仅作笔记。

学习可靠数据传输框架如下图:

运输层为应用层提供的服务可以抽象为:数据可以通过一条可靠的信道进行传输。借助于可靠信道,传输数据比特就不会受到损坏(例如由0变为1,或由1变为0)或者丢失,而且所有的数据都是按照其发送顺序进行交付。这也是TCP向调用它的因特网应该所提供的服务模型。可靠数据传输协议(reliable data transfer protocol)就是用来实现这种服务抽象的。在上面的框架中,rdt表示可靠数据传输协议,udt表示不可靠数据传输协议。应用层调用rdt_send()函数将数据交给运输层,随后运输层调用网络层的不可靠传输协议将分组交给网络层。在接收端,当分组从信道的接收端到达时,调用rdt_ecv()函数。当可靠数据传输协议想要向应用层交付数据时,调用deliver_data()函数完成。

构造可靠数据传输协议

可靠数据传输协议是比较复杂的,为了能更好的理解该协议,在前面提到的框架的基础上,我们先以最理想的方式去构造它的实现,然后再一步步改进直到最后得到一个完美的可靠数据传输协议。

   1. 经完全可靠信道的可靠数据传输:rdt1.0

构造可靠数据传输协议最完美的情况是较低层的信道是完全可靠(即分组会按顺序到达接收端并且不会受损)的,这个最简单的协议称为rdt1.0。在这个简单的协议中,一个单元数据与一个分组没差别。所有分组都是从发送方流向接收方,因为有了完全可靠的信道,接收方接收到的数据不会出现差错,也就不需要反馈任何信息给发送方。

在rdt1.0协议中,发送端通过rdt_send(data)事件接收来自较高层的数据,产生一个包含该数据的分组,并将分组发送到信道中;在接收端,通过rdt_rcv(packet)事件从较低层信道接收一个分组,然后从分组中取出数据并将数据上传给较高层。

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

rdt1.0中的完全可靠的底层信道是不存在的,更现实的模型是分组中的比特可能受损的模型,在分组的传输、传播或缓存的过程中都可能会出现比特差错,这个协议称为rdt2.0。在此模型中,比特可能受损但分组顺序不会打乱,即发送的分组仍会按照其发送的顺序接收。

比特受损的分组就好像A和B打电话时由于信号原因导致A没听到B说的话,一般情况A会说"padon me?"即抱歉,再说一遍;而当A听完B说的某句话的时候可能会说"ok"即嗯,表示听到了。以上场景就使用了肯定确认(positive acknowledgment)和否定确认(negative acknowledgment),使用在协议中就可以让接收方告诉发送方哪些内容被正确接收了,哪些内容接受有误需要重新传一次。在计算机网络环境中,基于这样重传机制的可靠数据传输协议称为自动重传请求(Automic Repeat reQuest,ARQ)协议

ARQ协议需要依靠以下三种协议功能来处理存在比特差错的情况:

  • 差错检测。要处理比特差错首先要知道哪些分组出现了比特差错,这时就需要一种机制来检测哪些分组出现了比特差错。类似UDP数据报中的检验和字段,一般的差错检测技术要求有额外的比特从发送方发送到接收方。
  • 接收方反馈。发送方和接收方一般情况都是在不同的端系统上运行,因此要想让发送方了解分组是否有问题以及哪些分组有问题就需要接收方提供明确的反馈信息给发送方。前面提到的肯定确认(ACK)和否定确认(NAK)就是这种反馈的例子,例如当前分组接收正常则反馈一个1,分组有比特差错则反馈一个0。
  • 重传。接收方收到有比特差错的分组时,发送方将重传该分组。

在rdt2.0协议中,发送端通过rdt_send(data)事件接收来自较高层的数据,产生一个包含该数据并且带有检验和的分组,并通过udt_send(packet)操作发送该分组。然后发送方等待来自接收方的反馈,如果收到一个ACK分组,则发送方知道该分组被接收方正确接收,协议返回继续等待上层的数据;如果收到一个NAK分组,该协议重传上一个分组并继续等待接收方的反馈。需要注意的是,当发送方等待接收方的反馈时,不能从上层获得更多的数据,即只有当发送方接收到接收方的ACK反馈并且协议返回时才能出现rdt_send()事件。由于这种行为,rdt2.0这样的协议被称为停等(stop-and-wait)协议。

rdt2.0协议有一个不能忽略的问题是当接收方像发送方发送的反馈(ACK或NAK)受损了,发送方将不知道接收方是否正确接收了上一次发送的分组数据。解决这个问题的一个方法是在发送方接收到模糊不清的ACK或NAK分组时,就重传当前数据分组。然而这种方法又在发送方和接收方的信道中引入了冗余分组(duplicate packet),冗余分组的出现让接收方不知道它发送的ACK或NAK分组是否被发送方正确接收,也就不知道当前分组是上一个分组的重传还是一个新的分组。解决这个新问题的一个简单的方法是发送方对其发送的分组进行编号,然后在数据分组中添加一个新的字段来存储这个序号,几乎所有现有的数据传输协议都是用的这种方法。对于停等协议,1比特长度的序号就够了,即当前分组序号为0,下一个分组序号为1,下下一个分组序号为0,下下下一个分组序号再为1。接收方只需要检查序号就可以判断收到的分组是否是一次重传。例如,发送方发送序号为0的分组到信道中,由于物理原因导致该分组出现了比特差错,接收方通过校验检验和检测出该分组有比特差错后向发送方发送一个NAK分组,而该NAK分组在信道中受损导致模糊不清,发送方接收到该分组后重传序号为0的分组,接收方接收到这个分组发现与上一次接收到的分组的序号相同,判定为重传。

rdt2.0协议可以再次优化成为一个无NAK的可靠数据传输协议。无NAK的意思是接收方不会发送给发送方NAK响应,要实现这种结果,只需要接收方收到一个受损的分组时发送一个对上次已经正确确认的分组的ACK分组(该ACK分组需要有分组序号,否则发送方无法判断是否为同一分组)取代NAK分组,这样发送方收到两个同一分组的ACK就知道接收方没有正确接收到跟在被确认了两次的分组后面的分组。

   3. 经具有比特差错的丢包信道的可靠数据传输:rdt3.0

现实中,发送方向接收方发送的分组除了可能出现比特差错外,还可能会丢失,即我们常说的”丢包“。rdt3.0协议即增加了检测丢包和丢包后的处理功能。

在rdt3.0中,让发送方负责检测和恢复丢包工作。假定发送方传输一个数据分组,该分组或接收方对该分组的ACK发生了丢失,这两种情况下发送方都接收不到应当来自接收方的响应。如果发送方等待足够长的时间来确定分组已经丢失,这时只需要重传分组即可。发送方最少需要等待的时间为:发送方与接收方的一个往返时延+接收方处理一个分组所需要的时间,如果等待时间比这个时间短,那在发送方看来所有分组都丢失了以至于一直重发第一个分组。等待时间过短或过长都会导致一些问题,最理想的协议是可以让发送状态尽快从丢包中恢复过来。因此等待时间需要发送方慎重选择。如果在这个时间内发送方没有收到ACK,则重传该分组;如果一个分组或接收方发送的ACK没有丢失,但往返的时延较大以至于超过了发送方设置的等待时间,那么发送方依旧会重发该分组。这就引入了冗余数据分组,但冗余数据分组的问题在rdt2.0协议中就使用序号解决了。

为了实现基于时间的重传机制,需要一个倒计时定时器,在一个给定的时间量过后,可中断发送方。在rdt3.0协议中,发送方需要做到:1)每次发送一个分组时便启动一个定时器。2)响应定时器中断。3)终止定时器。

在rdt3.0协议中,发送方向接收方发送数据在只考虑丢包的情况时(不考虑比特差错,rdt2.0已经完全解决了比特差错)可能会发生以下几种情况:

  • 发送方发到接收方的分组没有丢包情况;
  • 发送方到接收方的某个分组丢失,在发送方倒计时定时器超时时重发该分组;
  • 接收方发送给发送方某个分组的响应ACK丢失,在发送方倒计时定时器超时时重发该分组;
  • 发送方发送到接收方的某个分组和其ACK往返时延较大(ACK分组在到达发送方之前倒计时定时器便超时了),发送吃重发该分组。

流水线可靠数据传输协议

rdt3.0协议是一个正确的协议,即它可以完成一个可靠数据传输协议的工作。但由于其是一个停等协议(发送方发送完一个分组等待接收方ACK分组时不能从上层获得更多数据),发送方绝大数时间都是在等待接收方的响应,因此性能可以说很糟糕、

解决停等协议性能问题的一个简单方法是:不以停等的方式运行,允许发送方发送多个分组而无须等待确认。由于许多从发送方向接收方输送的分组可以被看成是填充到一条流水线中,因此这种技术被称为流水线(pipelining)。流水线技术对可靠数据传输协议带来如下影响:

  • 必须增加序号范围。rdt3.0协议由于是停等协议,同一时间只会有一个分组或该分组的响应在发送方到接收方的信道中传输,因此只需要0和1表示序号即可。而在流水线技术中可能会有多个分组在信道中输送,要知道哪个或哪些分组有比特差错或丢失就必须为他们创建唯一的序号。
  • 协议的发送方和接收方两端不能不缓存多个分组。发送方最少要缓存那些已发送但没有确认的分组,接收方需要缓存已正确接收的分组。

序号的范围和发送方接收方缓存分组的数量取决于数据传输协议如何处理丢失、损坏和延时过大的分组。解决流水线的差错恢复有两种基本方法:回退N步和选择重传。

回退N步(Go-Back-N,GBN)

在回退N步(GBN)协议中,允许发送方发送多个分组而不需要等待确认,但它也受限于在流水线中未确认的分组不能超过某个最大允许数N。下图显示了发送方看到的GBN协议的序号范围。

其中基序号定义为最早未确认分组的序号,下一个序号定义为最小的未使用序号(即下一个待发分组的序号)。从图中可以看出序号可以通过分组的状态分为四个范围,小于基序号的的序号对应的是已经发送并且已被确认的分组(淡粉色),大于等于基序号且小于下一个序号的序号对应的是已经发送但未被确认的分组(紫色),淡蓝色序号对应的则是将要被发送的并且有可用序号的分组,而淡绿色则表示无可用序号的分组,这些分组直到基序号分组被确认才会被分配序号。

那些已被发送但还未被确认的分组的许可序号范围可以被看成是一个在序号范围内长度为N的窗口。随着协议的运行,该窗口在序号空间向前滑动,因此,N常被称为窗口长度,GBN协议也常被称为滑动窗口协议。限制发送的且未被确认分组的长度为N的原因有多个,一个是流量控制,另一个是拥塞控制。这些在后面的文章会详细介绍,此处不再赘述。

在GBN协议中,发送发必须响应以下三种类型的事件:

  • 上层的调用。当上层调用rdt_send()时,发送方首先检查发送窗口是否已满,即是否有N个已发送但未被确认的分组。如果窗口未满,则产生一个分组并将其发送,并相应的更新变量;如果窗口已满,发送方缓存这些数据或使用同步机制允许上层在仅当窗口不满时再调用rdt_send()。
  • 接收ACK。在GBN协议中,对序号为n的分组的确认采取累计确认(cumulative acknowledgment)的方式,表明接收方以正确接收到序号为n的以前的(包括n)的所有分组。 
  • 超时事件。如果超时,发送放重传所有已发送但还未确认的分组。当发送方开始发送第一个分组时便启动一个定时器,如果收到一个ACK,但仍有已发送但未被确认的分组,则定时器被重新启动。

如果一个序号为n的分组被正确接收到,并且按序(即上一次正确接收的分组的序号为n-1),则接收方为分组n发送一个ACK,并将该分组中的数据交付给上层。并为最新按序接收到的分组重新发送ACK。之前提到GBN协议是采用累计确认的方式,即接收方如果正确接收到了序号为k的分组并交付,则表示所有序号比k小的分组都已经交付。

在GBN协议中,接收方会丢弃所有的失序分组。例如,接收方已经正确接收到了为序号n-1的分组,结果由于序号为n的分组丢失,接收到了序号为n+1的分组,这时接收方会丢弃n+1分组。当然,接收方也可以选择将n+1分组缓存起来,等到正确接收了序号为n的分组并交付给上层后再将序号为n+1的分组交付给上层,但需要注意的是,如果分组n丢失了,发送方会重传所有已发送但未确认的分组,也包括序号为n+1的分组。

为了更好的理解GBN协议,假如现在发送方使用窗口长度为4个分组的GBN协议向接收方发送数据,可能的一种情况以及GBN协议的处理如下图:

分组2丢失后,接收端接收到的分组3,4,5都被认为是失序分组,因此直接丢弃并发送ACK1。直到发送方分组2超时,发送方重传分组2后的所有分组。

选择重传(Selective Repeat,SR)

GBN协议允许发送方发送多个分组填充流水线,避免了停等协议的信道利用率低的问题,但也引来了其他的效率问题。例如当窗口长度和带宽时延都很大时,单个分组的差错会引起GBN大量重传分组,随着信道差错率的增加,流水线可能被这些不必要重传的分组充斥。例如窗口长度为1000,带宽时延足够大(大到窗口的1000个分组可能同时在信道中传输),如果分组2就丢失了,等到发送方已经发送完1000个分组时分组2超时,这时包括分组2在内的999个分组都要重传。999个重传的分组中有998个本不该重传的分组,无疑会使得性能降低。

而选择重传协议通过让发送方仅重传那些它怀疑丢失或受损的分组而避免了不必要的重传。这种个别的、按需的重传要求接收方逐个地确认正确接收的分组。SR接收方将确认一个正确接收的分组而不管其是否按序。失序的分组将被缓存直到所有丢失分组(序号比正确接收的分组小的分组)都被接收为止,这时才会将一批分组按序交付给上层。

SR发送方的事件与动作如下:

  1. 从上层接收数据。这一事件与GBN协议相同,当从上层接收到数据后,SR发送方检查下一个可用的序号,如果序号位于发送方窗口内,则将数据打包发送;否则要么将数据缓存,要么将其返回给上层以便以后传输。
  2. 超时。同样使用定时器来防止丢失分组,但每个分组都有属于自己的逻辑定时器,因为超时后只能发送一个分组。
  3. 收到ACK。收到ACK后,如果该分组序号在窗口内,则SR发送方将那个被确认的分组标记为已接收。如果该分组的序号等于窗口中最早未确认的序号(基序号),则窗口基序号向前移动到具有最小序号的未确认分组处。此时如果窗口移动了,并且有序号落在窗口内的未发送分组,则发送这些分组。

由于SR接收方可能要缓存分组,因此SR接收方也具有类似GBN协议中发送方的序号范围,同样具有窗口并且与SR发送方长度相同。但需要注意的是SR发送方和SR接收方的窗口的移动可能并不一致,如果现在还不能理解这句话,可以等下看例子。SR接收方的事件与动作如下:

  1. SR接收方的窗口内的分组被正确接收。在此情况下,收到的分组落在接收方的窗口内,该分组的ACK被回送给发送方。如果该分组以前没收到过,则缓存该分组。如果该分组的序号等于接受窗口的基序号,则该分组以及以前缓存的序号连续的分组交付给上层。然后,接收窗口按向前移动分组的编号向上交付这些分组。
  2. 接收到比接收方窗口的基序号小的序号的分组。在此情况下,必须产生一个ACK,即使该分组是接收方以前已经确认过的分组。因为此情况是发送方的窗口移动慢于接收方窗口的时候,发送方的基序号小于接收方的基序号。例如,发送方基序号为1,接收方基序号为2,接收方接收了序号为1的分组并发送ACK,但该ACK丢失了。随后发送方分组1超时,重新发送该分组,如果接收方不再次产生ACK,发送方的窗口就不会再移动了。
  3. 其他情况。忽略该分组。

在SR协议中,某个分组丢失的情况以及SR协议的处理可能如下图,其中红色表示序号在窗口内的分组。

对以上SR操作需要做以下几点说明:

  • 发送方和接收方的窗口长度都为4,因此在发送方发送完分组3且没有收到任何ACK时窗口满了,导致分组4无法发送;
  • 接收方在接收分组0和分组1时由于分组序号等于接受窗口的基序号,因此直接交付给上层,发送ACK并移动窗口(SR接收方动作第一条);
  • 由于分组2丢失,接收方收到分组1后接收到的是分组3,序号不连续,因此分组3到分组5都被缓存起来,直到分组2接收到才会将分组2,3,4,5交付给上层;
  • 发送方必须收到序号为基序号的ACK时才能向前移动窗口(例如收到ACK0,ACK1时),因为分组丢失或ACK丢失导致基序号分组的ACK未收到时,则窗口不移动(例如分组2);
  • 由于发送方在接收到基序号的ACK时才会移动窗口,而接收方在收到序号为基序号的分组时就会移动窗口,因此发送方的窗口一般都会比接收方窗口移动较慢,上例中可以明显看出来。结合上图考虑以下场景:分组1被接收方收到,接收方窗口移动,基序号变为2,发送ACK1。然后ACK1丢失,直到分组1超时发送方才会重发分组1,如果接收方不再响应分组1(即不向比接收窗口基序号小的分组发送ACK),发送方窗口将永远不再移动。因此接收方即使接收到已经确认过的窗口序号比接收窗口基序号小的分组,也要产生一个ACK(SR接收方动作第二条)。

如果看到这里有些懵逼,建议从头重新读一次直到弄懂,笔者在学习这个知识点是也是想了好久才总结下来的。

在之前我们从来没有纠结过序号长度和窗口长度问题,那么是否这两个长度随意呢?当然不是的,例如下图中的例子。有限序号为0,1,2,3,窗口长度为3。发送方发送了分组0,1,2后窗口满了,接收方全部正确接收发送了ACK0、ACK1、ACK2,但这三个ACK都缺失了。此时发送方窗口未移动,而接收方窗口移动到了3 0 1。等到分组0超时,发送方重发分组0,但对于接收方来说并不能分辨这个序号为0的分组是第一次序号为0的分组的重传还是第二次序号为0的分组的第一次发送。

在讨论窗口长度和序号的关系之前,我们先来讨论一下发送窗口和接收窗口的长度关系,这有利于我们后面对与窗口长度和序号的讨论。在本篇文章的例子中,发送方窗口长度和接收方窗口长度都是相同的。发送方窗口长度大于或小于接收方窗口长度会发生什么情况呢?

  • 当发送方窗口长度大于接收方窗口长度时,拿上图的例子来说,如果发送方窗口长度仍为3,接收方窗口长度为2。发送方分别发送了分组0、1、2,结果分组2首先到达了接收方,但由于序号2未在接收方窗口内,因此此分组被丢弃。如果发送方窗口长度为100,接收方窗口长度为50,那被丢弃的可能就不止1个分组了。这无疑导致了资源的浪费。因此发送方窗口长度最好小于等于接收方窗口长度;
  • 当发送方窗口长度小于接收方窗口长度时,每当有序号为基序号的分组到达接收方时接收方窗口长度都会向前移动,而发送方只有在收到序号为基序号的ACK时才会向前移动,接收方窗口移动速度只会比发送方快。因此在任何情况下,接收方比发送方窗口多出来的长度永远不会用到。

因此,发送方的窗口长度和接收方的窗口长度应该一致。接下来讨论窗口长度和序号的关系,由于接收窗口移动速度要比发送窗口移动速度快,因此只有在发送方的窗口的基序号与接收方窗口的末端序号重合时,才会发生无法分辨重合序号是重传还是第一次发送的情况。例如发送方向接收方发送一段包含16个分组的数据,有限序号为0、1、2、3、4、5、6、7,窗口长度为5。则在分组0、1、2、3、4都被接收方确认但ACK0、1、2、3、4都丢失时,发送方窗口和接收方窗口的情况如下:

0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
0 1 2 3 4 
          5 6 7 0 1

此时,发送方重传分组0或1时,接收方便无法分辨是重传还是新分组的第一次发送。而如果窗口长度为4,在分组0、1、2、3都被接收方确认且ACK0、1、2、3都丢失情况下,发送方窗口和接收方窗口的情况如下:

0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
0 1 2 3 
        4 5 6 7

由于发送方窗口与接收方窗口中的序号没有重合的,因此即便发送方重传,接收方同样不会有分辨问题并且会返回相应分组的ACK。细心观察可以发现,发送方和接收方的窗口长度加起来只要不超过有限序号空间大小,就不会发生序号重合问题,也就不会发生序号无法分辨重传还是新分组的第一次发送问题。又由于发送方和接收方的窗口长度相同,因此我们可以得出一个结论:窗口长度应当为小于等于有限序号空间大小的一半。

总结

以下表格完全可以总结之前介绍的可靠数据传输机制:

机制用途和说明
检验和用于检测在一个传输分组中的比特错误
定时器用于在一个分组或其ACK丢失超市时重传该分组
序号

用于为从发送方流向接收方的数据分组按顺序编号。所接收分组的序号间的空隙可使接收方检测出丢失的分组。具有相同序号的分组可使接收方检测出一个分组的冗余副本

确认接收方用于告诉发送方一个分组或一组分组已被正确地接收到了。确认报文通常携带着被确认的分组或多个分组的序号。确认可以是逐个的或累积的,这取决于协议
否定确认接收方用于告诉发送方某个分组未被正确地接收。否定确认报文通常携带着未被正确接收的分组的序号
窗口、流水线发送方也许被限制仅发送那些序号落在一个指定范围内的分组。通过允许一次发送多个分组但未被确认,发送方的利用率可在停等操作模式的基础上得到增加。窗口的长度可根据接收方接收和缓存报文的能力、网络中的拥塞程度或两者情况来进行设置
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值