第三章第三节 可靠数据传输原理
信道一般指连接信号发送方和接收方的传输线路,包括双绞铜线等物理媒体。在实际的网络传输中,信道是不可靠的,在其上传输的分组可能会损坏或丢失,甚至相对次序都不能保证。
TCP 这一 运输层协议 则提供了可靠的数据传输框架解决上述问题,其向上层(应用层)提供面向连接的可靠的服务。而TCP的下层是网络层,网络层提供的尽力而为的服务,也就是说不提供任何质量保证。简单来说,TCP实现以下两个功能,即“可靠”的概念:传输的数据比特不会受到损坏或丢失;所有数据都是按照其发送顺序进行交付的。
在本节中我们仅讨论单向数据传输(unidirectional data transfer)的情况,介绍可靠传输协议、流水线协议、回退N步、选择重传。
Outline
- 可靠服务的模型
- 经过完全可靠的信道的 rdt 1.0
- 经有比特差错信道的可靠数据传输 rdt 2.0
- rdt 2.0 自动重传请求协议
- rdt 2.1 解决 ACK/NAK 分组受损的引入序号的协议
- rdt 2.2 只用 ACK 的协议
- 经具有比特差错的丢包信道的可靠数据传输:rdt 3.0
- rdt 3.0 的性能分析
- 可靠数据传输协议的技术
Notes
## 可靠服务的模型
- 上图就说明了用于数据传输协议的各个接口。
- 运输层通过
rdt_send()
函数,调用数据传输协议的发送方,把数据交付给接收方的较高层。 - 发送方的运输层调用
udt_send()
函数,将分组发送到不可靠的信道。 - 当分组从信道的接收端到达接收端时,将调用
rdt_recv()
。 - 当
rdt
协议向较高层交付数据时,调用deliver_data()
。
- 运输层通过
## 经过完全可靠的信道的 rdt 1.0
有限状态机(FSM) 可以表示有限个状态及在这些状态之间的转移和动作等行为的数学模型,下图即表示发送方和接收方的有限状态机,底层信道是完全可靠的,发送方和接收方有各自的FSM,每个FSM都只有一个状态。
(图解:FSM描述中的箭头指示了协议从一个状态变迁到另一个状态。引起变迁的事件显示在横线的上方,事件发生时所采用的运动显示在横线的下方。FSM的初始状态用虚线表示。)
- 在发送端,只通过
rdt_send(data)
事件接收来自较高层的数据发送请求。在完成一次数据发送请求中需要两个动作:- 产生一个包含该数据的分组(经由
make_pkt(data)
产生) - 然后将该分组通过
udt_send(packet)
发送到信道中
- 产生一个包含该数据的分组(经由
- 完成这两个动作后,重新返回原始状态,继续等待来自较高层的数据发送请求。
- 在接收端,rdt 通过
rdt_rcv(packet)
事件从底层信道接收一个分组。在一次数据接收过程中同样需要两个动作:- 从分组中取出数据(经由
extract(packet, data)
产生) - 然后将数据上传给较高层(通过
deliver_data(data)
动作)
- 从分组中取出数据(经由
- 和发送端一样,接收端完成这两个动作后也重新返回原始状态,继续等待从底层信道接收分组。
- 值得注意的是,rdt的发送端是由较高层应用的调度产生的,接收端协议是由较底层协议的过程调度产生的
- 在这个协议中,一个单元数据和一个分组没有区别。
- 因为信道可靠,接收方也不需要提供任何反馈信息给发送方
- 假定了接收方接收数据的速率能够与发送方发送数据的速率一样快,所以接收方也没有必要请求发送方慢一点发送。
## 经有比特差错信道的可靠数据传输: rdt2.0
现在我们可以假设在信道传输过程中分组中的比特可能受损了,在这种比特可能受损的情况下,来看一下如何构造可靠数据传输协议 rdt 2.0。注意现在仍然假定所有发送的分组(即使受损)将按照其发送的顺序被接收,同时信道中的分组也不会丢失。
接收方收到分组以后,可以向发送方发送肯定确认 (ACK) 或否定确认 (NAK) 。这些控制报文可以让发送方知道哪些内容被正确接收,哪些内容接收有误并因此需要重复。这种基于重传的可靠数据传输协议被称为自动重传请求 (Automatic Repeat reQuest, ARQ) 协议。
ARQ协议需要三个功能来处理分组出错的情况:
- 差错检测。我们需要一种机制能够使接收方检测什么时候出现了比特差错。
- 比如 UDP检验和 字段就是为了这个目的。
- 这些技术要求有额外的比特从发送方发送到接收方,而这些比特将存放在 rdt 2.0 数据分组的分组检验和 字段中。
- 接收方反馈。发送方要了解接收方是否正确接收分组的唯一途径就是让接收方提供明确的反馈信息
- rdt 2.0 协议将从接收方向发送方回送 ACK 或 NAK 分组。
- 这些分组在理论上只需要一个比特长,比如用 0 表示 NAK,用 1 表示 ACK。
- 重传。如果接收方收到了受损的分组,发送方必须重传该分组。
【rdt2.0 自动重传请求协议】
下图为rdt 2.0 的有限状态机描述图,该数据传输协议(自动重传请求协议)采用了差错检测、肯定确认与否定确认。
- 在发送端左边的初始状态中,发送端协议正等待来自较高层传下来的数据。当触发
rdt_send(data)
事件时:- 通过
sndpkt = make_pkt(data, checksum)
产生一个包含待发送数据且带有校验和的分组 - 然后将该分组通过
udt_send(sndpkt)
发送到信道中
- 通过
- 执行完上述的两个动作后,发送端的状态变迁为“等待接收接收端的 ACK 或 NAK 分组”。即转变为右侧状态,接下来根据接收端的响应不同会有不同的变迁方案:
- 如果收到了一个 ACK 分组(
rdt_rcv(rcvpkt) && isACK(rcvpkt)
),那么发送方知道最近一个分组已经被正确接收,因此协议返回左边状态,继续等待下一次由较高层传下来的数据发送请求 - 如果收到了一个 NAK 分组(
rdt_rcv(rcvpkt) && isNAK(rcvpkt)
),那么发送端知道接收端接收到的分组是受损的,所以调用udt_send(sndpkt)
重新发送该分组,然后状态不变,继续等待接收接收端的 ACK 或 NAK 分组。
- 如果收到了一个 ACK 分组(
在上述协议中,当发送方处于等待ACK或NAK状态时,它不能从上层获得更多数据。这样子的协议被称为停等协议 (stop-and-wait)。
- rdt 2.0 的接收端仍然只有一个状态。状态变迁取决于收到的分组是否受损,有两种方式:
- 如果收到的分组受损,即
rdt_rcv(rcvpkt) && corrupt(rcvpkt)
,则返回 NAK 分组 - 如果收到的分组完好,即
rdt_rcv(rcvpkt) && notcorrupt(rcvpkt)
,则返回 ACK 分组
- 如果收到的分组受损,即
- 处理完后仍然返回自身这个状态,继续等待下一次从底层接收分组并处理。
【rdt2.1 解决 ACK/NAK 分组受损的引入序号的协议】
rdt2.1 似乎是一个可以在有比特差错信道上正常工作的可靠数据传输协议了,但仔细想想,我们没有考虑 ACK 或 NAK 分组受损的情况。
处理ACK/NAK分组损坏的方法有以下三种:
- 增加控制信令: 即发送方请求接收方重复ACK或NAK分组。但新的控制信令也可能受损。
- 增加足够的校验和比特: 使发送方不仅可以检测差错,还可以恢复差错。对于会产生差错却不会丢失分组的信道,可以直接解决问题。
- 重传当前数据分组: 发送方收到损坏的ACK或NAK分组时,重传当前数据分组。这种方法在信道中引入了冗余分组 (duplicate packet)。 这导致接收方不知道接收到的分组是新数据分组的还是一次数据重传。
想要解决这个问题,我们需要在数据分组中添加一个新的字段,然后让发送端对其数据分组编号,将发送数据分组的序号放在该字段中。于是,接收端只需要检查序号就可以确定收到的分组是否是一次重新传送的分组。
我们把引入序号的协议称为rdt 2.1
,FSM图如下。
- 按照上图来描述一下 rdt 2.1 协议发送端的状态变迁过程:
- 首先由较高层触发
rdt_send(data)
事件,通过sndpkt = make_pkt(0, data, checksum)
产生一个序号为 0,包含待发送数据且带有校验和的分组,接着通过udt_send(sndpkt)
将其发送到信道中,然后状态变迁为“等待接收接收端的 ACK 或 NAK 0” - 当发送端收到了一个来自接收端的分组数据:
- 如果该分组数据受损,或者接收到的是 NAK 分组,那么通过
udt_send(sndpkt)
重新传送刚才的序号为 0 的分组到信道中 - 如果该分组完好且收到的是 ACK 分组,那么发送端知道接收端已经成功接收了刚才发送的序号为 0 的分组,此时发送端状态变迁到等待较高层传下来的数据发送请求
- 如果该分组数据受损,或者接收到的是 NAK 分组,那么通过
- 接着再次由较高层触发
rdt_send(data)
事件,通过sndpkt = make_pkt(1, data, checksum)
产生一个序号为 1,包含待发送数据且带有校验和的分组,接着通过udt_send(sndpkt)
将其发送到信道中,然后状态变迁为“等待接收接收端的 ACK 或 NAK 1” - 当发送端再次收到了一个来自接收端的分组数据:
- 如果该分组数据受损,或者接收到的是 NAK 分组,那么通过
udt_send(sndpkt)
重新传送刚才的序号为 1 的分组到信道中 - 如果该分组完好且收到的是 ACK 分组,那么发送端知道接收端已经成功接收了刚才发送的序号为 1 的分组,此时发送端状态变迁到等待较高层传下来的数据发送请求(即回到本状态机的初始状态)
- 如果该分组数据受损,或者接收到的是 NAK 分组,那么通过
- 首先由较高层触发
- 接着再来描述一下 rdt 2.1 协议接收端的状态变迁过程:
- 首先在初始状态上,接收端等待着接收由发送端发来的序号为 0 的分组数据
- 接着由
rdt_rcv(rcvpkt)
从底层信道接收了一个分组数据:- 如果该分组受损(即
rdt_crv(rcvpkt) && corrupt(rcvpkt)
),那么由sndpkt = make_pkt(NAK, checksum)
产生一个附带校验和的 NAK 分组,接着由udt_send(sndpkt)
发送回发送端 - 如果该分组失序(即
rdt_rcv(rcvpkt) && notcorrupt(rcvpkt) && has_seq1(rcvpkt)
),那么由sndpkt = make_pkt(ACK, checksum)
产生一个附带校验和的 ACK 分组,接着由udt_send(sndpkt)
发送回发送端 - 如果该分组完好且顺序正确(即
rdt_rcv(rcvpkt) && notcorrupt(rcvpkt) && has_seq0(rcvpkt)
),那么通过extract(rcvpkt, data)
和deliver_data(data)
将分组数据上传给较高层程序。接着,由sndpkt = make_pkt(ACK, checksum)
产生一个附带校验和的 ACK 分组,由udt_send(sndpkt)
发送回发送端
- 如果该分组受损(即
- 接下来等待序号为 1 的分组的处理过程与上面类似,不再赘述
【rdt2.2 只用ACK的协议】
协议rdt 2.1 使用了肯定确认和否定确认。当接收到正确的分组时,接收方发送一个“肯定确认”,当接收到受损的分组时,发送一个“否定确认”。如果不发送NAK,而是对上次正确接收的分组发送一个ACK,也可以实现一样的效果。发送方如果接收到两个一样的ACK,就说明接收方没有正确接收到跟在被确认两次的分组后面的分组。
我们把只使用ACK的协议称为rdt 2.2
,其FSM图如下。
具体 rdt 2.2 的流程因为和 rdt 2.1 基本类似,故不赘述。
## 经具有比特差错的丢包信道的可靠数据传输:rdt 3.0
在现实的网络环境中,除了比特受损外,底层信道还会丢包;有很多可能的方法可以解决丢包问题,这里我们让发送方负责检测和恢复丢包工作。
假定发送端传输一个数据分组,该分组发生丢失 或者 接收端对该分组的 ACK 发生了丢失。在这两种情况下,发送端都收不到应当到来的接收端的响应。所以,如果发送端愿意等待足够长的时间以确定该分组缺失已丢失,则它只需要重传该数据分组即可。
从发送端的观点来看,重传是一个万能灵药。为了实现基于时间的重传机制,需要一个倒数计时器 (countdown timer),在一个给定的时间量过期之后,可中断发送方。发送方需要做到:1)每次发送一个分组(包括第一次分组和重传分组)时,就启动一个定时器;2)相应定时器中断;3)终止定时器。
下图是rdt 3.0
的发送方FSM,该协议运行在可能发生出错和丢失的信道上。
rdt 2.2 协议中的接收端有限状态机描述图仍然适用于 rdt 3.0 协议,下面我仍然用文字来简要描述一下上图中的发送端发送分组流程:
- 首先由较高层触发
rdt_send(data)
事件,通过sndpkt = make_pkt(0, data, checksum)
产生一个序号为 0,包含待发送数据且带有校验和的分组,接着通过udt_send(sndpkt)
将其发送到信道中并启动定时器,然后状态变迁为“等待接收接收端的 ACK 0” - 当发送端在“等待接收接收端的 ACK 0”的时候:
- 如果收到了受损的分组(即
corrupt(rcvpkt)
)或者收到了 ACK 1(即isACK(rcvpkt, 1)
,也就是收到了自己发送的上一个分组的 ACK),则直接忽略 - 如果定时器时间到,则由
udt_send(sndpkt)
重新发送该分组并重新启动定时器 - 如果收到了完好的分组且 ACK 为 0,那么发送端知道接收端已经成功接收了刚才发送的序号为 0 的分组,直接停止定时器,此时发送端状态变迁到等待较高层传下来的数据发送请求
- 如果收到了受损的分组(即
- 注意在继续等待从较高层传下来的数据发送请求的过程中,如果收到了任何分组数据包,都直接忽略,因为它们一定是冗余的
- 接下来继续重复上面的流程,只是改变了序号,不再赘述
## rdt 3.0 的性能分析
假设有两台主机,分别位于美国西海岸和东海岸,端对端延时 大约为 30ms,假定它们通过一条速率 R 为 1Gbps 的信道相连。包括首部字段和数据的分组长 L 为 1KB (8000 bits),所以发送一个分组进入 1Gbps 链路实际所需
如果发送方在 t=0 时刻开始发送分组,则在 8μs 后,最后1bit进入了发送端信道。经过 15ms 后,分组的第一个 bit 到达接收端;在 15.008ms 时刻,分组的最后一个 bit 到达接收端。假设接收端的 ACK 产生和发送不占用时间,则再经过 15ms 以后,即t=30.008ms,发送端接收到接收端的ACK,发送端可以发送下一个分组。
在 30.008ms 内,发送方的发送使用了0.008ms。我们定义信道利用率为:发送方实际忙于发送比特到信道的时间与发送时间之比,则停止等待协议的发送方利用率为:
在1G连路上每30毫秒才发送一个分组(33KB/s),这就是网络协议限制了物理资源的利用的例子。
## 可靠数据传输协议的技术
校验和、序号、定时器、肯定和否定确认分组,这些技术都在协议的运行中起到了必不可少的作用。现在 rdt 3.0 已经是一个功能正确的协议,但因为它的本质仍然是停等协议,所以效率实在捉急。在本笔记的下篇中,将介绍流水线可靠数据传输协议、回退 N 步协议以及选择重传协议,最终将会得到一个可靠并且效率较高的协议实现方法。