在计算机网络中,可靠的数据传输,是一个较为重要的问题,最近在看书(Computer Networking A Top-Down Approach),发现 rdt(Reliable Data Transfer) 大概经历了这样的变化。
上图是两个主机相应进程之间的通信过程,左边是发送端,右边是接收端
rdt 1.0
在 1.0 版本中,我们将数据的传输信道理想化,视为完全可靠,不丢包,不损失bit ,在这样的情况下,发送端发送数据,接收端直接接收,并不考虑丢包,超时这些问题,下图是对应FSM(状态图)
该协议中,都是直接发送,直接接收。
rdt2.0
果然,大家应该都意识到了,怎么可能这么理想化呢,要是传输通道完全可靠,我们讨论的意义何在呢?
在 rdt2.0 中,我们将传输通道视为有可能发生比特错误。
有可能发生比特错误,这就是说,数据在传输中不会发生丢包的现象,但是会存在一部分比特错误的情况,于是我们引入:
差错检测:使用检验和,换句话讲,就是检验发过来的包有没有错误
接收方的反馈:接收方返回 NAK 或者 ACK ,分别对应数据错误和数据正确,这个也可以理解,总要告诉发送端,你发过来的是对还是错
上图中的corrupt ,在字典里面是 “腐烂,腐化” 的意思,在这里可以理解成包出错。
很容易理解,发送端发出数据,并等待接收端反馈,如果返回 NAK ,表示数据出错,重新发送数据,至于接收端怎么检验数据,不用说,当然是使用检验和啦。
rdt2.1
经过大家思考,终于发现了 rdt2.0 的致命缺陷,原来接收端返回的值也可能会出错啊,万一NAK出错变成ACK了呢
于是诞生了 rdt2.1 ,该协议在 2.0 基础上增加了一个序号值(在这里,该序号在当前协议中只使用 0 和1 ,交替排列),这样一来,发送端和接收端都有了两种序号状态, 0 和 1 。
我们来用自己的语言,组织一下上面的逻辑:
发送端在 0 序号时发送数据包,接收端此时期待 0 序号的数据包,如果数据发送时发生 bit 受损,此时接收端直接通过检验和发现错误,并返回 NAK ,发送端接到 NAK 的返回值,然后重新发送 0 序号数据包。(但是注意,这完全和 rdt2.0 没有任何分别,这是一种理想的状态!!!)
更错误的状况是,接收端成功接收,但是返回 ACK的时候发生了比特翻转,变成了 NCK ,这才是我们讨论的重点!!接下来,我需要画一张图,来理清楚当返回错误的时候,怎么通过序号来判断并重传数据。
这样就解决了 NAK , ACK 返回值有可能出错的问题。
rdt2.2
由于相关开发人员的吹毛求疵,他们觉得需要返回 NAK , ACK 两种状态可能太麻烦了,就将其全部改为ACK 只是返回的时候顺便返回序号。
可以看到,接收端收到包,不管正确与否,都返回 ACK ,同时附上序号,这个序号嘞,就是数据包发送过来时的序号。另外的参照上图,相信同学们都能有所收获。
rdt3.0
可以说,在处理数据出错方面,上面的协议都做得很好了,但是,我们忽略了一个很大的问题,万一数据不是出现错误,而是直接丢失了呢!!这就是我们俗称的丢包了,于是,我们的 rdt3.0 千呼万唤始出来了。
所以在这里,我们假设的是最贴近真实的情况,数据传输通道发送和返回的过程中不仅会出错,而且还会丢包!
因此,我们引入一个新的机制——超时重传。先不考虑数据出错与否,数据发送出去,会有两种丢包可能:1.发送出去的时候丢失,接收端并未收到 。2.接收端收到了,但是在反馈 ACK 的时候,数据包丢失。在这两种情况下,发送端都是什么反馈都没有收到!!,那问题就简单了,我们设置一个时间间隔,超过时间没有收到,重新发送数据即可。(事实上还有很多问题,比如时间间隔多少合适,这里暂且不管)
上图是 rdt3.0 发送方的 FSM 图,就拿右上角的状态举例,此时发送端等待接收方返回的带有“0”序号的 ACK ,它有三种行为:
- 第一种,过程中未丢包,但是数据比特出错或者不符合序号,和我们讨论过的 rdt2.2 差不多,那么此时就没有任何动作,毫无作为就好了,等到时间间隔一到,当做超时处理,重发数据。
- 第二种,是真正的丢包了,所以时间一到,重新发送。
- 第三种最理想,啥事没有,一切正常,跳到下一个状态,等待发送下一个包。
上面我加粗了一个不符合序号,细心的读者可能发现了,的确,在这里又是大有文章。
这里的不符合序号,与期待的序号不符,有两种情况:
- 接收端反馈过程中,代表序号的那个 bit 错误,进行了翻转——这就和 rdt2.2 中一样
我们上面引入了超时机制,但是有一种情形,万一我发出去的数据包并没有丢失,只是它跑的太慢而已呢??那么时间一到,发送端误以为丢包,重新发了一遍咋办??这就造成,接收端才收到你晚来的数据 0 号,还给你个 0 ,这时候你重传的 0 号包接着又到了,接收端又给你一个0 ,作为发送端,我会先后收到两个 0 ,就注定后面一个 0 会被期待 1 号的状态捕捉,这时发送端启动第一种处理方式——不作为,啥都不做。
上面便是示意图。
到此为止, rdt 3.0 已经被认为是一个较为完美的可靠传输协议了,但是还有着种种的不足,比如:
- 效率太慢,由于停等方式的存在,一个包没处理好,发送端会一直等着。
- 超时机制的时间间隔怎么确定?长了不行,太慢,短了么,又会经常有重复发包的情况。
- …….
…….
这些我将会在后续的博客上阐述,敬请期待吧~~