面试题:TCP是如何实现可靠传输的?
TCP/IP可靠传输的基础是滑动窗口协议和连续ARP协议(累积确认/累积应答),配合流量控制和拥塞控制,使得整个传输过程保证:
- 传输信道无差错
- 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据(通过累计确认、超时重传、拥塞控制三大模块保证)
滑动窗口协议和连续ARQ协议
关于连续ARP协议:
- 为了保证顺序性,每一个包都有一个ID。在建立连接的时候,会商定起始的ID是什么,然后找到ID一个个发送。
- 为了保证不丢包,对于发送的包都需要进行应答,但是这个应答也不是一个个来的,而是会在应答某个之前的ID,表示都收到了,这叫做累积确认/累积应答
停止等待ARQ协议(已经不用了)
- “停止等待”就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
- 停止等待协议对信道的利用率不高,因为发送方每发送一个消息,都需要等待确认消息回来,只要确认消息没有正确地到达,就一直等;
可以分为四种情况
无差错情况
如上图(a)所示,表示无差错情况下:
- A 发送分组 M0,发完就暂停发送,等待 B 的确认。
- B 收到了M0就向 A 发送确认。
- A 在收到了对 M0的确认后,就再发送下一个分组 M1。
- 同样,在收到 B 对 M1 的确认后,再发送 M2。
超时重传
如上图所示,是传输过程中出现了差错的情况下的超时重传机制。
出现差错分为两种:
- M1在传输的过程中丢失了
- B 接收 M1 时检测出了差错,直接丢弃 M1
上面两种情况下,B 都不会发送任何消息给 A。
为了解决上述场景遇到的问题,可靠传输协议是这样设计的:
- A只要超过了一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组,这就叫做超时重传
- 要实现超时重传,就要在没发送完一个分组就设置一个超时计时器,如果超时计时器到期之前就收到了对方的确认,就撤销已设置的超时计时器。
必须保证以下几点:
- A发送完一个分组后,必须暂时保留已发送的分组副本,只有收到确认后才能清除分组副本
- 分组和确认分组都必须进行编号
- 超时计时器设置的重传时间应当比数据在分组传输的往返时间更长一些
确认丢失
接收方向发送方发送确认帧1,但是在传输中丢失,接收方在固定时间内没有收到确认帧,会再超时重传1帧。
看个例子:
- 如上图中所示,假设 B 所发送的对 M1 的确认丢失了,那么 A 在设定的超时重传时间内没有收到确认。原因有很多,可能是 A 自己发送的分组出错、丢失,或者是 B 发送的确认丢失了。 因此 A 在超时计时器到期后就要重传 M1。
- 现在应注意 B 的动作。假定 B 又收到了重传的分组 M1。这时应采取两个行动:
- 丢弃这个重复的分组 M1,不向上层交付
- 向 A 发送确认。不能认为已经发送过确认就不再发送,因为 A 之所以重传 M1就表示 A 没有收到对 M1 的确认
确认迟到
- 发送方发送完之后确认帧一直没有传过来,就会超时重传一个0号帧,接收方收到0号帧之后发现刚刚已经收到过0号帧了,就会把重复发送的0号帧丢弃掉,再返回给发送端一个确认帧;
- 同理,如果发送端之前已经收到0的确认帧,那么对于后来又收到的0的确认帧,收到就丢弃。
看个例子:
- 如上图(b)所示,假设传输过程中没有出现差错,但 B 对分组 M1 的确认迟到了。A 会收到重复的确认。
- 对重复的确认的处理很简单:收下后就丢弃。 B 仍然会收到重复的 M1,并且同样要丢弃重复的 M1,并重传确认分组。
连续ARQ协议
停止等待ARQ协议效率比较低,所以引入了ARQ(Automatic Repeat reQuest:自动重传请求)。
- 连续ARQ协议可以提高信道的利用率。它引入了滑动窗口,通过滑动窗口可以批量发送数据,发送方维持一个发送窗口,凡是位于发送窗口内的分组都可以连续发送出去,而不需要等待对方确认。
- 接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。
如下图:
- 滑动窗口大小为6,可以批量发送6个消息;当收到1和2的确认消息之后,滑动窗口就可以往后移两个为止,将窗口内还没有被发送的数据发送出去。
-
对于滑动窗口,如果每一个报文都需要确认,那么确认消息的开销会非常大,因此对于滑动窗口并不需要对每个报文都进行确认,而是采用累计确认的方法。
-
假设现在同时发送了1到6这6个报文,在某一时刻发送方收到第5个报文的确认消息,如果采用累计确认的方法,我们就默认1到5这5个消息都收到了,因此会把窗口往前推动五格。
累积确认方式
为了提高信道的利用率,实际上采用了流水线传输的方案。这时就需要使用连续ARQ协议和滑动窗口协议。发送方和接收方各自维持着发送窗口和接受窗口,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。接收方一般采用累计确认方式,即接收方不必对收到的分组逐个发送确认,而是可以在收到几个分组后,对按序到达的最后一个分组发送确认,这样就表示:到这个分组位置的所有分组都已经正确收到了。
看个例子:
由图可见,描述一个发送窗口的状态需要三个指针P1、P2、P3
- P3−P1=A 的发送窗口(通知窗口)
- P2−P1=已发送但尚未收到确认的字节数
- P3−P2=允许发送但尚未发送的字节数(可用窗口或有效窗口)
解析一:此时B没有收到序号为31的数据,因此只能对按序收到的数据中的最高的序号给出确认,即此时B的确认号还是30(因为B没有收到序号31的数据,只能发送序号30的确认号)
解析二:此时B收到了序号31的数据,那么B会进行如下处理:
- 把31~33的数据交给主机,B删除这些数据缓存
- 接着把接收窗口向前移动3个序号,同时给A发送确认号33
A收到B的确认号后,就可以把发送窗口向前滑动3个序号,但P2指针不变
解析三:A在继续发送完序号42~53的数据后,指针P2P2和P3P3重合。发送窗口内的序号都已经用完,但还没有收到确认。由于发送窗口已满,可用窗口已减小到了零,因此必须停止发送。此时A设置了超时计时器,超时计时器到期时就重传这部分数据,重置超时计时器,知道收到B的确认为止。如果A收到的确认号落在发送窗口内,那么A就可以使发送窗口继续向前滑动,并发送新的数据。
发送缓存与发送窗口、接收缓存与接收窗口
发送缓存用来暂时存放(即发送窗口):
- 发送应用程序传送给发送方TCP准备发送的数据
- TCP已经发送出但尚未收到确认的数据
接收缓存用来暂时存放:
- 按序到达的、但尚未被接收应用程序读取的数据
- 未按序到达的数据
发送缓存和接收缓存小结
- A的发送窗口根据B的接收窗口设置,但是二者并以总是一样大
- 对于不按序到达的数据应如何处理,TCP彼岸准并无明确规定(一般都是临时保存在接收窗口中,等到字节流完整接收后再按序提交给上层的应用进程)
- TCP要求接收方必须有累积确认的功能,这样子可以减少传输开销