阿里 Java 二面必问:8 张图带你解决所有 TCP 可靠传输问题
1. TCP 可靠性如何保证?
信道可靠:用三次握手、四次挥手保证连接正确;
数据正确:分区编号、校验和、超时重传;
传输控制:流量控制、拥塞控制
2. 重传机制
TCP 可靠传输方式是序列号与确认应答。当传送数据包丢失时,会用重传机制解决。常见重传机制有超时重传、快速重传、SACK、D-SACK。
2.1 超时重传
TCP 协议要求在发送端每发送一个报文段,就启动一个定时器并等待确认信息;接收端成功接收新数据后返回确认信息。若在定时器超时前数据未能被确认,TCP 就认为报文段中的数据已丢失或损坏,需要对报文段中的数据重新组织和重传。
2.2 快速重传
如果收到一个失序的报文段时, TCP 需要立刻产生一个 ACK,这个 ACK 不应该被延时,目的在于让对方知道收到一个失序的报文,并告诉对方自己希望收到的报文 seq,我们不知道这个重复的 ACK 的原因,因为还是会等待少量的重复 ACK 到来,如果连续收到 3 个或者 3 个以上的 dup ACK,就被判断这个报文被丢失了,于是就需要立即重传丢失的数据段,这个地方不用等待定时器溢出。
3. 滑动窗口协议
在 TCP 协议中,发送方和接受方通过各自维护自己的缓冲区。通过商定包的重传机制等一系列操作,来解决不可靠的问题。为了增加网络的吞吐量,想将数据包一起发送过去,便产生了“滑动窗口”协议 。
滑动窗口实现
在图中,我们可看出灰色 1 号 2 号 3 号包已经发送完毕,并且已经收到 Ack。这些包就已经是过去式。4、5、6、7 号包是黄色的,表示已经发送了。但是并没有收到对方的 Ack,所以也不知道接收方有没有收到。8、9、10 号包是绿色的。是我们还没有发送的。这些绿色也就是我们接下来马上要发送的包。 可以看出我们的窗口正好是 11 个。后面的 11-16 还没有被读进内存。要等 4 号-10 号包有接下来的动作后,我们的包才会继续往下发送。
正常情况
可以看到 4 号包对方已经被接收到,所以被涂成了灰色。“窗口”就往右移一格,这里只要保证“窗口”是 7 格的。 我们就把 11 号包读进了我们的缓存。进入了“待发送”的状态。8、9 号包已经变成了黄色,表示已经发送出去了。接下来的操作就是一样的了,确认包后,窗口往后移继续将未发送的包读进缓存,把“待发送“状态的包变为”已发送“。
丢包情况
有可能我们爆发过去,对方的 Ack 丢了。也有可能我们的包并没有发送过去。从发送方角度看就是我们没有收到 Ack。
发生的情况:一直在等 Ack。如果一直等不到的话,我们也会把读进缓存的待发送的包也一起发过去。但是,这个时候我们的窗口已经发满了。所以并不能把 12 号包读进来,而是始终在等待 5 号包的 Ack。
超时重发
这个 Ack 是要按顺序的。必须要等到 5 的 Ack 收到,才会把 6-11 的 Ack 发送过去。这样就保证了滑动窗口的一个顺序。 这时候可以看出 5 号包已经接受到 Ack,后面的 6、7、8 号包也已经发送过去已 Ack。窗口便继续向后移动。
4. 流量控制
如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。原理这就是运用 TCP 报文段中的窗口大小字段来控制,发送方的发送窗口不可以大于接收方发回的窗口大小。
接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段, 通过 ACK 端通知发送端;窗口大小字段越大, 说明网络的吞吐量越高;接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;发送端接受到这个窗口之后, 就会减慢自己的发送速度;如果接收端缓冲区满了, 就会将窗口置为 0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端
问题 1:窗口关闭是什么?怎么解决?
现象:接收方若没有缓存足够使用,就会发送零窗口大小的报文,此时发送放将发送窗口设置为 0,停止发送数据。之后接收方有足够的缓存,发送了非零窗口大小的报文,但是这个报文在中途丢失,那么发送方的发送窗口就一直为零导致死锁 。
解决:TCP 连接方收到零窗口通知就启动计时器,发送窗口探测报文。探测时,若接收窗口仍为 0,那接收报文一方就重新启动计时器;若不是 0,死锁局面打破。
问题 2:糊涂窗口综合征是什么?怎么解决?
接收方腾出几个字节告诉发送方窗口大小,而发送方会义无反顾发送这几个字节,导致报文利用率很低,这就是糊涂窗口综合征
解决方式:
1.让接收方不通告小窗口给发送方
2.让发送方避免发小数据——延时处理
5. 拥塞控制
TCP 的拥塞控制由 4 个核心算法组成:
“慢启动”(Slow Start)
“拥塞避免”(Congestion voidance)
“快速重传 ”(Fast Retransmit)
“快速恢复”(Fast Recovery)
为了在发送端调节所要发送的数据量,定义了一个“拥塞窗口”(Congestion Window),在发送数据时,将拥塞窗口的大小与接收端 ack 的窗口大小做比较,取较小者作为发送数据量的上限。
5.1 慢启动
TCP 源端一开始并不知道网络资源当前状况,因此新建立的 TCP 连接不能一开始就发送大量数据,而只能逐步增加每次发送的数据量。
源端按 cwnd 大小发送数据,每收到一个 ACK 确认,cwnd 就增加一个数据包发送量。为了防止 cwnd 增长过大引起网络拥塞,还需设置一个慢开始门限 ssthresh 状态变量。ssthresh 的用法如下:
当 cwnd<ssthresh 时,使用慢开始算法
当 cwnd>ssthresh 时,改用拥塞避免算法
当 cwnd=ssthresh 时,慢开始与拥塞避免算法任意
5.2 拥塞避免
拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍。这样拥塞窗口按线性规律缓慢增长。
无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认,虽然没有收到确认可能是其他原因的分组丢失,但是因为无法判定,所以都当做拥塞来处理),就执行慢开始算法,把慢开始门限 ssthresh 设置为出现拥塞时的发送窗口大小的一半。然后把拥塞窗口设置为 1。如下图:
5.3 快重传与快恢复
快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。
快重传配合使用的还有快恢复算法,有以下两个要点:
①当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把 ssthresh 门限减半。但是接下去并不执行慢开始算法。
②考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将 cwnd 设置为 ssthresh 的大小,然后执行拥塞避免算法。
问题 1:流量控制和拥塞避免有何区别?
流量控制是端到端的控制,例如 A 通过网络给 B 发数据,A 发送的太快导致 B 没法接收(B 缓冲窗口过小或者处理过慢),这时候的控制就是流量控制,原理是通过滑动窗口的大小改变来实现。
拥塞控制是 A 与 B 之间的网络发生堵塞导致传输过慢或者丢包,来不及传输。防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不至于过载。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络性能有关的所有因素。
来源:https://www.toutiao.com/i6996837150528389663/
评论