什么是可靠?
不错、不丢、不乱
可靠数据传输协议
可靠数据传输对应用层、传输层、链路层都很重要
网络Top-10问题
信道的不可靠特性决定了可靠数据传输协议(rdt)的复杂性
可靠数据传输可以从不同的角度来看,比如从a图是从提供的服务来看:
红线网上是应用层,在发送方是一个发送的进程,接收方是一个接收的进程。红线下面的传输层为上层提供可靠的数据传输服务,虽然从底层来看仍然是不可靠的信道,但是传输层通过设计的策略来实现可靠。
进一步的,从服务的实现角度(b图)来看,由于底层仍然是一个不可靠的信道,可靠数据的实现要在传输层借助算法实现,包括了发送方的可靠数据传输协议部分和接收方可靠数据传输协议部分。这两部分相互配合,共同实现可靠的数据传输协议。
因为下层仍然是不可靠的信道,因此传输层向下调用的是udt_send方法,即unreliable data send,发送到信道中。而此时传输层对上层来看是一个可靠的传输协议,因此上层调用的是rdt_send,即reliable data send。
总而言之,这就相当于在传输层实现了一套可靠传输协议,并对上层透明,对上层应用来说它所面向的就是可靠数据传输,尽管底层的信道仍然是不可靠的,也就是a图中从服务角度来看。
从图中可以看到,在传输层和不可靠信道(比如网络层)之间是双向的箭头,也就是说在不可靠信道上实现可靠的数据传输,并不是一个简单的单向数据流动就能实现,需要双向的控制消息的流动。
只考虑单向数据传输,但是控制信息双向流动。利用状态机(Finite State Machine, FSM)刻画传输协议
接下来,我们从易到难,逐步递进来分析一下实现可靠数据传输的方案和流程。
首先我们假定一个简单的场景,即信道只会产生位错误,而不会有其他错误产生。
**场景:**传输中可能会发生位错误,比如把某个0翻转为1,把某个1翻转为0。目前的场景认为信道只可能发生这种错误,不会发生分组丢失等其他问题,并且数据是按顺序到达的。这种场景下来研究位错误时,可靠传输机制如何来实现。
接收方:
接收方如何判断接收的数据是否发生了位错误?
利用校验和检测位错误
如何从错误中恢复?
接收方显示的反馈给发送方一些控制消息:ACK/NAK
基于上述这种重传机制的rdt协议被称为ARQ(Automatic Repeat reQuest)协议
基于上述思想,设计如下的有限状态机:
发送方有限状态机
此时,发送方存在两个有限状态:Wait for call from above和Wait for ACK or NAK。
第一个状态表示等待上层调用,比如应用层向传输层发送数据(rdt_send),接收到数据后会调用make_pkt进行打包,此时会携带是数据的checksum校验和,最后调用udt_send发送到接收方。
第二个状态表示等待ACK/NAK控制状态的返回,即停—等协议。
上述发送方的有限状态机即表示:接收上层协议的调用,并将数据打包发送到接收方,同时,停止等待接收方ACK/NAK状态信息的返回。如果接收到的是NAK状态,则重新执行udt_send将数据重传到接收端。如果接收到的是ACK状态,则回到第一个状态,等待下次调用。
接收方有限状态机
Rdt2.0有什么缺陷?
如果ACK/NAK消息发生错误/被破坏(corrupted)会怎么样?
如何解决重复分组问题?
发送方有限状态机
此时发送方的有限状态机设计如下所示:
假设这里使用两个序列号,0和1,可以看到状态机的状态翻倍了,因为要停止等待不同序列号下数据的ACK/NAK。这里在make_pkt方法中携带了每个数据包对应的序号,此时对于上述重传可能产生分组重复的问题,相同序号的数据包会在接收到被丢弃。
接收方有限状态机
可以看到有限状态机的状态也翻倍了,分别等待接收序号为0和1的数据包。
发送方
接收方
实际上,我们并不需要两种状态来表示接收方是否正确接收数据。
与rdt2.1功能相同,但是只使用ACK,如何实现?
”校验和 + 序列号 + ACK + 重传”够用吗?
如果发送方发送的一个数据分组在信道中丢失了,此时发送方和接收方分别会采取什么反应?
此时,接收方没有收到数据,则什么也不做,仍然一直等待接收数据。而发送方则会一直等待接收方反馈的状态信息(状态机停留在Wait for ACK状态)。在这种情况下,这个分组就完全丢失了,变成不可靠传输了。
又或者,数据分组到达接收方,但是接收方返回的ACK在信道中丢失了,此时发送方也会一直等待下去,此时这个协议就不能正常工作了,也就是上面的Rdt2.2的机制就不够用了
怎么解决上述问题?
解决方案也很简单:超时重传。
即发送方等待“合理”的时间,如果超时没收到ACK,则重传。
同时,由于存在分组或ACK只是延迟而不是丢失,导致的超时后,数据重复的问题,仍然采用上述Rdt2.1机制中介绍的序列号的方法:
Rdt3.0有限状态机图例:
从上图中可以看出:
Wait for ACK
状态中,多了一种状态转移条件,即timeout。如果timeout超时,则触发udt_send重传数据。Rdt3.0工作的几种场景
右图的timeout即显示了当丢失了packet时,等待超时时间后,发送方重传packet,此时当数据包丢失时,rdt3.0仍能正常工作。
C图表示了当接收方返回的ACK1丢失时,发送方再等待timeout时间后,对数据包进行了重传。此时,由于接收方会接收到两个pkt1,即重复的packet,由于有序号机制,接收方发现存在两个相同序列号的数据包,会丢弃刚刚收到的这个pkt1的数据包,并重发ACK1。在这种场景下,即使ACK丢失了,rdt3.0机制仍能正常工作。
D图表示了ACK没有丢失,只是延迟到达的场景。此时,发送端在等待timeout时间后对pkt1数据包进行了重传,然而此时发送方才收到ACK1的返回。这种情况接收方也会出现接收到重复分组的问题,但是由于存在序列号机制,接收方仍然可以根据序列号丢掉重复的分组。而发送方也会接收到两个重复的ACK1的返回,根据上图中发送方的有限状态机状态可以发现,由于在前一个ACK1到达后,发送端就发送pkt0的数据了,因此停留在等待ACK0响应的状态,因此第二个ACK1不会引起状态机的状态转移,相当于也会被接收方丢弃。(因为是示例,所有图中只有两个状态,实际上TCP在解决这个问题时,会丢弃返回的ACK序号小于当前发送数据包序号的ACK)。因此这种场景下,即使ACK重复,rdt3.0机制仍能正常工作。
到此我们的rdt3.0机制中解决了之前多个场景下的问题,引入了序号机制、超时重传机制等。
Rdt3.0能够正确工作,但相对来说性能很差,特别是当带宽速率很高的时候
示例:比如存在一个1Gbps的链路,由于链路比较长,存在15ms端到端的传播延迟,假定发送的每个分组大小是1KB
则此时的分组的在该链路上的传输时间为:
即需要8s,可以将一个分组从这个链路上发送出去。由于rdt3.0是一个停等协议,因此发送完数据分组后,会停止等待ACK的返回。
由于端到端的传播延迟为15ms,因此rtt约等于30ms(一个来回)。
发送方利用率:发送方发送时间百分比:
相当于在1Gbps链路上,每30ms才发送一个分组 -> 33KB/sec
网络协议限制了物理资源的利用
rdt3.0导致的性能较差的原因,本质上就是因为停止等待协议。
上面我们通过自己设计可靠传输协议的方式,来层层递进地展示可靠性传输算法所要解决的问题和实现原理。更进一步的,借助于有限状态机的状态更新来标识可靠性传输步骤,其中如果能吃透有效状态机在可靠性传输过程中的状态转换,会很有助于我们自己理解可靠性传输的机制和原理。