通俗易懂的例子告诉你TCP/IP如何保证可靠性

TCP的可靠体现在哪?

要对TCP实现的可靠性进行分析,我们首先要知道这个可靠性指的是什么?我觉得主要是三个方面:
1. 保证传输的分组比特位不出错
2. 保证传输的分组不丢失
3. 保证传输的分组不乱序


那么TCP如何保证这三个方面不出问题呢?在不同的底层信道上,TCP应该提供的保证也不相同,我们针对不同的信道逐个进行分析

可靠信道上的可靠数据传输

由于在可靠信道上进行数据传输,因此并不用担心数据传输会出问题,发送方和接收方正常收发数据就可以了
无分组比特位出错问题
无丢失分组问题
无分组乱序问题


经具有比特差错信道的可靠数据传输

在这种信道上,我们假设 <只会>出现比特位出错问题,<不会>出现分组丢失和乱序问题

那么对应的,发送方和接收方应该要能采取哪些措施来应对呢?
首先<接收方>,它在收到错误分组时,要有辨别的能力,这样还不够,它还得告诉发送方,它收到的分组有没有问题,无则回ACK,有则回NAK
其次是<发送方>,在收到接收方的反馈之后,它要根据反馈做出是否重传分组的决定
总结一下 发送方和接收方应该具备的功能:
1. 接收方差错检测
2. 接收方反馈
3. 发送方重传
基于这样重传机制的可靠数据传输协议称为
自动重传请求( Automatic Repeal reQuesl, ARQ) 协议

看起来好像万无一失了,错误的分组可以重新发送传输,然而一个不可忽视的问题是如果接收方的反馈在传输过程中发生了错误呢?发送方如何根据错误的反馈做出决定
最简单的解决办法是,发送方看到错误或者模糊不清的反馈就直接进行重传但是会出现新的问题就是接收方如何识别这是个重传的分组还是一个新的分组呢?
发送方也想到了这个问题,于是它和接收方进行协商,告诉接收方,在每个分组的头部加上一个序号字段,1表示这个分组是重传的分组,而0表示这是一个新的分组,这样就解决了分组可能出现错误的问题

或许还是没有表达清楚,我举个例子捋一下吧

A为发送方,B为接收方
A告诉B,我给你寄的快递我都做了标记啊
1表示你不满意退回的快递,我给你重发的,0表示前面我给你寄的快递你都满意,我继续给你新发的其它你要求的

情景一:快递是坏的,要求重发
A:给B寄了一个快递,标记为0
B:打开A寄的快递一看,是坏的,标记NAK要求重发
A:看到B寄的快递,眉头一紧,只看清A,不知道到底是啥意思,不管三七二十一,选一个同款的没毛病的标记为1重发
B:收到A重发的很满意,标记ACK
OVER
情景二:快递是好的,但是A看不清反馈,同款重发
情景三:快递是好的,A看清了反馈,发其它款新的

<情景二和情景三大家自行脑补吧,觉得我还有没描述清楚的或者错误的大家评论区提出来啊,觉得描述清楚了不妨给我个赞,很期待大家的反馈哦>

另一个或许更好的解决办法是序号字段不用0或者1表示,而是为每个分组定制一个编号,用这个编号来替代0和1来表示每个分组的序号字段,这样接收方在进行反馈时,不需要再返回NAK了,而是不管收到正确分组还是错误分组都直接返回ACK,这个ACK里包含一个序号字段,表示最近正确接收的一个分组编号,这样发送方在收到ACK之后,就知道自己最近的一个被正确接收的分组是哪个了,来决定是重传还是直接发新的分组

这一大段或许没有表达清晰,我来举个例子把

A为发送方,B为接收方
A告诉B,我这次不用0或者1表示是否是重传了啊,我干脆给每个快递定制一个编号,你看到编号就知道我是重传还是发新的,
你给我的反馈就发最后一个让你满意的快递的编号就行
A:给B发一个编号为20的快递
B:收到编号20的快递,很满意,回信ACK20表示对编号20的快递很满意
A:收到B的回信,继续给B发编号21的快递
B:打开一个,发现坏了,继续回信ACK20
   (对编号21的快递不满意,距离最近的满意的快递是编号20的
A:打开信一看,知道B对21号快递不满意了,重新发21号快递
B:这次打开快递很满意,回信ACK21
A:知道B对21号快递满意,继续给B发22号快递
OVER
这里给大家举的例子是A能看清B的反馈的情况下,如果A看不清B的反馈,按上述机制也是可解决的,大家可自行脑补

<觉得我还有没描述清楚的或者错误的大家评论区提出来啊,觉得描述清楚了不妨给我个赞,很期待大家的反馈哦>


经具有比特差错的丢包信道的可靠数据传输

在这种信道上,不仅会发生<比特位错误>,还可能发生<丢包甚至乱序>

前面我们一直默认分组一定能送达,虽然送达的是正确的还是错误的并不一定,到这里我们不妨仔细想想,如果这时接收方给发送方的回馈在半路丢了呢?发送方怎么办?没错,发送方会一直等待,但是我们心里都清楚,发送方等不到接收方的反馈了

这时,一个很简单的解决办法就是定一个计时器,到了一定时间就提醒发送方停止等待,并重新发送分组,但是正由于这么多停止等待操作,发送方的效率变得异常低下
为了改进效率,引入了类似于<流水线>的解决办法,使发送方发送多个分组而无需等待确认,进而提高效率,我们分别来看看这两个办法:

1. 回退 N 步 (GBN) 协议(滑动窗口协议):
在这个协议中,允许发送方发送多个分组而无需等待确认,但它也受限于未确认的分组数不能超过某个最大允许数 N,即发送方窗口的大小,如果窗口满了,它必须告诉上层,窗口已满,暂不能发送分组。

举个例子,比如这个窗口大小N为3,用S表示它剩余可继续发分组的数目,那么在一开始,N为3,S为3,它可以一次把编号为0,1,2的分组发出去(当然4,5,6也可以,数目不超过3就行),这个时候S就为0了,不能继续往外发分组,因为已经发出去3个分组,那么到什么时候它可以继续发分组呢?有两种可能:
1. 等待一定时间都没有等到接收方的确认,它自己清空窗口,重新发送0,1,2分组,这里需要注意的是,这个一定时间是有规定的,它应该是从<上次收到确认>开始计时,到了<一定时间>之后,重传所有没有收到确认的分组,因此,只有一个计时器
2. 接收方发来确认,经过接收方确认的分组编号从窗口中清除出去,但是需要注意的是,接收方采取的是<累积确认>的方式,也就是说,如果接收方确认了编号为20的分组,那么就说明20号以前的包括20号的都被正确接收,那么问题来了,如果发送方在收到接收方对编号为20的分组确认之后,继续给接收方发编号为21,22的分组,但是此时22号比21号先到接收方怎么办呢?答案是接收方会直接把22号的分组丢弃,不做任何确认,等21号到达时,再返回对21号的确认,发送方在等待一段时间后,重新发送22号分组

到这里,可能有点乱,没有突出重点,我再来捋一下:
对发送方来说:
1. 包含一个大小为N的窗口,每次只能发送编号在窗口内的分组,而且不需要等待前一个的确认就可以直接发下一个
2. 对于已发出去的分组,到了<一定时间>后没收到确认需要进行重发
3. 收到确认后,窗口才进行前移,比如窗口大小为3,一开始发出去0,1,2分组,收到0分组的确认后,窗口前移包含的分组就变成了1,2,3分组
对接收方来说:
1. 接收方没有窗口,但是它包含一个目标分组编号,比如它之前已经收到了20号以前的分组,那么接下来就只能收20号的分组,大于20号的提前到达了都直接丢弃
2. 采取累积确认的方式,对20号进行确认,说明20号包括20号以前的都正确接收了
为了方便大家理解,我再举个例子:

A给B发快递,并且A有一个箱子,用来装接下来要发的快递,B有一个牌子,用来表示接下来要收的快递的编号
A:把编号123的快递放到箱子里,准备发给B
B:收到编号为1的快递,一看牌子,正好是1,收下快递,把牌子上的号码改为2,并写信给A回复
A:收到B的对1号快递的回复,把1号从箱子里拿出来,把4号快递放进箱子,并往B发出
  (现在在路上的快递有234,但是32快,接下来B收到的是3
B:收到3号快递,但是一看牌子,是2,毫不犹豫把3丢弃,啥也不干,等2
A:继续等回复
  (现在在路上的快递有24,没有3了,因为已经被B丢弃了
B:终于收到快递2,写信给A说收到了2号快递,牌子改为3,马上又收到快递4,一看跟牌子不一致,丢弃
A:收到了B的对2号快递的回复,把2号从箱子里拿出来,并把5号放进去,一看钟,
   发现34号快递已经发出去一个小时了,还没有回复,于是重发34号快递,并把5号快递也发出去
Over

细心的大家到这里肯定就会发现,接收方动不动就丢弃,发送方动不动就重发,这得多影响效率啊,也很占用网络资源吧,甚至导致堵塞,还有没有更好的办法呢?答案是当然有,接下来我们一起来讨论

2. 选择重传:
要讨论选择重传是怎样进行的,我们不妨把它与回退N步进行一个比较
首先是接收方:
1. 有了窗口,用于缓存能够接收的分组,比如窗口里序号为2,3,4,5,那么就说明窗口可缓存编号为2,3,4,5的分组,收到分组后,返回对应的确认,比如收到2号分组,就返回对2号分组的确认,那么问题来了,如果再次收到1号分组呢?也将给予返回<确认然后再丢弃>,而不是直接丢弃。这么解释可能还会有疑问,可以看到窗口已经从1,2,3,4移动到2,3,4,5了,说明1号分组已经被正确接收且返回确认了啊,怎么还有有1号分组呢?仔细想一下,如果之前返回给发送方1号分组的确认在中途丢了呢?发送方只能再次发送1号分组,因为它没收到确认啊,所以这个时候接收方必须再次返回确认
2. 收到乱序的分组后,不直接丢弃,而是缓存到窗口并返回对应的确认,比如还没有收到2号分组,却收到3号的,就不像回退N步那样直接丢弃,而是缓存起来,并返回对3号分组的确认,直到收到2号分组之后,返回对2号分组的确认,再移动窗口,变成4,5,6,7

然后是发送方:
1. 选择重传在计时器上做了改进,<回退N步>整个接收方都只有一个计时器,而<选择重传>每个分组都有计时器
2. 重传的时候并不是重传所有还未被确认的分组,而是看单个分组的计时器,如果这个分组的计时器到了一定时间了,就重传这个分组,比如窗口里有2,3,4,5这几个编号,3,4,5都收到了确认,2的计时器超时了,那就只重传2号分组,回想一下,如果是<回退N步>,在没有收到2号分组确认的情况下,可能收到3,4,5的确认吗?答案是不会,虽然是累积确认,但是它确实每收到一个分组,就给与一个确认,累积确认的意义在于,编号小的分组没有确认,不可能确认大的,确认了就表示,编号小的分组肯定也都收到了,而不是,多个分组累积到一次发送确认
3. 窗口的移动时机,<回退N步>在收到确认时就可以移动窗口,因为接收方采取的累计确认方式,编号大的确认好了,编号小的肯定确认好了,而<选择重传>并不能,因为窗口里可能 有 2,3,4,5这几个编号,可能先收到3号分组的确认, 就先标记一下,之后再收到2号分组的确认,就可以移动窗口了,变成4,5,6,7

可以看到,采取选择重传的方式效率会有很大的提高,只需要重传超时未收到确认的分组,而不是重传后面所有已经发送过的分组

就这样,TCP在不同信道的基础上,完美的解决了分组错误,分组丢失,分组失序的问题~

参考自《计算机网络自顶向下》,写了大概有五六个小时…在线求反馈,觉得写的不清楚的地方求求指出,我马上改进,觉得写的不错,也求求给点个赞让更多人看到哦!

你可能感兴趣的:(网络,面经)