啰嗦几句
TCP应该是计算机网络最重要的一块知识了,本篇博客作为TCP的前置文章,主要介绍了如何一步步在底层协议为不可靠传输的情况下实现可靠传输。但是可靠数据传输真实状况非常复杂,专业术语又多,《自顶向下》估计也是简单给我们介绍了大致全貌,其带领我们从新搭建了一个可靠数据传输协议,让我惊叹不已。小弟不才,阅读完这本书的此章之后,实在是无法另辟蹊径的介绍清楚可靠数据传输原理及其相关术语,只好将此章内容复读机一遍,用大白话解释一遍,实在羞愧。
应该记住的是可靠数据传输协议的 "模型"。
那么开始吧~
构建可靠数据传输
可靠的数据传输是一个一般性的问题,不单单只有传输层有这个问题,链路层及应用层都会出现,在底层为不可靠传输的情况下为上层提供可靠传输通道,将这种服务抽象出来就是可靠传输协议。
为了减少复杂度,我们只考虑单向传输,即发送方发送数据到接收方。
1.0版本
rdt1.0 底层为可靠通路1.0版本是假设底层通路为安全通路,最为简单。发送方将上层送来的数据分组打包后发送,接收解析后拿出数据传送到上层。
2.0版本(考虑数据分组受损)
现实中哪有这么好的事情,不可靠的事情多了去了......在2.0版本我们考虑在传输过程中会发生比特差错,就是数据包中的某个比特位从1变成了0或者0变成了1,通常是物理部件出现了毛病,这样该分组就受损了。那我们如何解决这个问题呢?
别急,首先作为接收方我们得有法子检查送过来的数据包是正确的还是出了差错,这个办法是为差错检验(链路层内容),总之是在数据中加入一些额外的比特位,我们就能知道传过来的数据是否出错(甚至可以纠错)。检查出错后,那我们肯定得让发送方在发送一次分组过来,送货上门把货整碎了还得了?于是我们也返回一个含有确认标志位的分组给发送方,如果成功收到了分组并且分组正确,就返回一个肯定确认(ACK)的分组,否则就返回一个否定确认(NAK)的分组,告诉发送方数据分组受损,发送方收到否定确认后重传一个分组给接收方,直到发送方收到接收方的ACK分组。
接收方返回确认控制报文让发送方知道是否正确接收的,接收错误则重传的机制,就叫做自动重传机制。
2.1版本(考虑确认报文受损)
2.0版本虽然解决了数据分组的受损的问题,但是引来了新的问题:确认报文在传输过程中受损了怎么办?这样发送方就无法得知接收方是否正确的接收到分组了。
确认报文受损
通常的解决办法是在发送方到接受方的通道中引入冗余分组:当接受到的确认报文无法正确解析时,重传一上一个分组即可。啥?听不清楚货有没有出问题啊,算了,再给你发一次货吧!
但是,这下轮到接收方出问题了,接收方可不知道自己的ack分组出问题了!!兄弟,你这是重传的还是新的分组?
为了解决这个问题,我们引入序号。发送方传过来的分组序号与上一个分组的序号一样,接收方就知道这是重传的分组。
2.2版本(加入冗余ACK)
同时我们还优化一下确认分组,现在已经不需要nak分组啦,返回的ack是上一个正确接收的分组序号,也能达到解决数据分组受损的问题。
总结一下,发送方发送具有序号的数据分组,接收方发送ack分组表示自己是否正确收到分组,ack内容为上一个正确接收的分组序号。
到此,我们对分组受损的解决方案已经出来了。当发送方收到了两个ack一样的确认报文,说明数据分组受损,就重发分组;当确认报文受损时,发送方重发分组,接收方收到两个序号一样的分组就知道该分组是重发的。
rdt3.0(考虑丢包)
除了分组受损外,还有可能的就是分组在传输过程中丢失了,通常是因为网络阻塞导致的。解决这个问题的方式也是重传,无论是数据分组还是确认分组丢包了,发送方在等待一段时间无果后,就重传数据分组。
为了实现重传机制,我们需要一个倒计时定时器。发送方发送一个数据分组时,启动定时器。定时器的生命周期是:启动定时器,中断定时器(执行动作),结束定时器。
定时器的时间 = 往返时间(RTT)+ 接收方处理时间
到此,可靠数据传输协议的功能已经正确了,我们能看到,发送方发送一个数据分组后,只有得到接收方的反馈或者计时器超时后才能进行下一步的操作,这被称作停等协议。由于停等协议的特性,发送的数据分组序号只需要0或者1就足够了,这被称为比特交替协议。
rdt3.0流水线
虽然rdt3.0 在功能上已经完整了,但是其性能太差。发送方发送完一个数据分组后的时间都是在等待反馈。为了优化性能,我们引入了流水线协议:发送方无须等待确认就可以发送多个数据分组。
为了适应新的协议,必须做出改变:
- 序号范围需要扩大,保证不会有两个数据分组序号一样(重传除外)
- 发送方和接收方都需要有一个缓存数组存放数据分组。(思考其作用)
- 流水线协议差错恢复有两种方式:回退N步(GBN)和选择重传(SR),不同的方式影响了序号范围和缓冲大小。
在阅读之前强烈建议看看这个小程序加深理解小程序
选择重传
我们从发送方和接收方两个角度来看:
发送方有一个发送缓存,可以保存将要发送的多个分组以及发送了但未确认的分组。分组按字节排序,即序号与分组是第几个字节有关。发送方有发送窗口(滑动窗口),只能发送发送窗口内的分组,我们将基序号定义为最早未确认分组的序号(图中send_base),即基序号前的分组都是已经确认成功发送的,基序号后的分组都是已发送但是尚未返回和未发送的分组。滑动窗口确保了流水线模式下的正确传输。
接收方也有接收窗口,接收基序号。接收窗口接收到rcv_base分组时,发送ack = rcv_base+1的确认分组到发送方,如果接收到的分组不是接收基序号的分组但在接收窗口中,缓存并发送ack为rcv_base的确认分组。
发送窗口在正确的发送分组后(正确的返回了ack),将窗口向右移至下一个基序号。
接收窗口接收到正确的分组后,也向右移动。将分组移交上层应用程序。
回退N步性能上存在问题,一般都是选择重传机制使用的多,有兴趣的小伙伴可以去查阅一下,这里就不讲了。
感觉选择重传讲的非常糟糕,学艺不精
网友评论