写点什么

TCP 的慢启动、拥塞避免、重传、快恢复乱七八糟总是记不清?11 个连环问让你一次性打通任督二脉

  • 2021 年 12 月 08 日
  • 本文字数:2374 字

    阅读完需:约 8 分钟

摘要:如果你的开发过程涉及数据传输,一直在重传、超时之类的方案里有困惑的话,不妨重新学一学可靠性最精致的 TCP 协议。


本文分享自华为云社区《TCP的慢启动、拥塞避免、重传、快恢复乱七八糟总是记不清?11个连环问让你一次性打通任督二脉》,作者: breakDraw。


TCP 的拥塞避免等机制对于初学者来说还是比较复杂的,工作中如果开发时偏应用层,那么大部分时候就会摸不到这个机制,感受也就没那么深了。但如果你的开发过程涉及数据传输,一直在重传、超时之类的方案里有困惑的话,不妨重新学一学可靠性最精致的 TCP 协议。


所以这里我抛去死记硬背的那堆概念,用 10 个连续的问题来学习这个机制,注意看的时候先自己思考一下如果是自己,会怎么设计,再去看实际的 TCP 设计,来理解它的精妙之处。

Q: 建立连接后,每次发送的报文数量是固定的吗?即每次都发 1 条或者 10 条?

A:不是。建立连接后,会先只发 1 条, 然后发 2 条,接着再发 4 条,逐步增加。这个过程叫 “慢启动”。这个 1、2、4 递增的数量被称之为 拥塞窗口 cwnd,可以理解为 TCP 希望刚开始,可以大胆点,不断加数量。但为了保险期间还是从 1 条还是倍增。

Q: 慢启动过程中,那么发送数量(拥塞窗口)什么时候不再倍增?是无限倍增吗?

A:不会无限倍增。当到达慢启动门限 ssthreshold 时,会变成每次都增加 1 条。这个过程叫拥塞避免过程, 也有叫他拥塞避免算法的,可以理解为 tcp 感觉到有风险了,于是开始慢慢地、小心翼翼地 1 条 1 条地添加发送条数。

Q:那么,当进入拥塞避免,每次+1 时,什么时候才会不再继续加?

A:随着每次发送的数量越发越多, 最终会超出带宽限制,于是就会有某条报文发生超时。有可能是发的中途丢了, 亦或者是返回的数据全阻塞住了,一条都回不来。当发送端检测到发生超时时,就会让 慢启动门限 ssthreshold = 当前拥塞窗口 cwnd/2,接着 cwnd 重新置为 1,从新开始 慢启动算法。这样的好处在于可以检测到每次发送的上限,动态调整发送窗口。上面的过程叫做 超时重传。注意发生超时重传时, cwnd 会重置成 1。

Q: 上面提到了超时, 那么 TCP 客户端是怎么判断报文发送超时的呢?

A:每次发送数据包的时候, 都会有一个相应的计时器,一旦超过 RTO(超时时间) 而没有收到 ACK, TCP 就会重发该数据包。没收到 ACK 的数据包都会存在重传缓冲区里,等到 ACK 后,就从缓冲区里删除。

Q:上面提到的超时时间 RTO 是怎么来的?万一设得太大可能导致很迟才能反应过来, 设得太小则可能导致每条都超时

A:通过“每次报文的往返时间样本”和“之前样本的偏差值”动态计算出来的。

  • RTT : 报文往返时间(指从发送到收到 ack 的时间)。每个报文发出后都有个定时器,收到后都会计算出一个 RTT 样本

  • RTTs: 加权平均往返时间,类似于一个估算的往返时间,实时在变。RTTs = (1-a) * RTTs + a * RTT 最新样本,即每次得到 RTT 样本后, 都会使用 a 这个占比去更新 RTTs。

  • RTTd: RTT 偏差加权平均值(就是用来计算超时时间应该比 RTT 多多少)RTTd = (1 - b) * RTTd + b*RTTs - RTT 最新样本,即每次会用新的 RTTs 以 b 的占比去更新一下 RTTd,并减去 RTT 样本

  • RTO : 超时重传时间等于平均往返时间 加上 4 倍偏差值,RTO = RTTs + 4*RTTd

Q: 如果发生重传,却还是没有收到 ack,那么最新的 RTT 样本应该怎么算?即你都收不到最新的 ack 了, RTT 难道取超时时间吗?

A:会使用 karn 算法: 发生重传时,不更新这次的 RTT 样。选用后面收到的 ack,修正 karn: 为了避免发生重传后,实际 RTT 都变慢了,导致一下子所有请求都超时, 会在发生重传时,把 RTO 假大 1 倍。

Q: 上面提到的 ACK 超时判断会不会太久了? 假如只是发的时候丢了中间部分报文而已, 但大部分报文 ACK 还能正常返回,也要一直等超时吗?

A:如果能正常接收其他报文的 ACK, 只是中间的部分报文丢了, 则有另一个办法。

接收端有一个冗余确认机制:

  1. 发送端 A 发送 1、2、3、4、5 四条

  2. 但是 B 接收端只收到 1、2、4、5,而 3 因为网络拥塞丢了。

  3. 于是 B 会发送 ack=3 而不是 ack=5 给 A。 这就是冗余确认机制,只发送缺失那部分的 ack,后面的 4 和 5 都不管。

  4. A 收到 ack=3 后, 继续发送 3、4、5、6、7, 结果 3 还是丢了。

  5. 于是 B 又发送 ack=3。


当 A 发现连续 3 次收到了 ack=3 时,就会觉察到不对劲,我都发 3 次了你还是说没收到,可你又能正常返回其他 ACK 给我,是不是我发的太多了?上面这个判断 3 次的重传算法叫“快重传”。于是 A 会马上进入 “快速恢复”。和之前类似,慢启动门限 ssthreshold = 当前拥塞窗口 cwnd/2。但是!! 新的拥塞窗口 cwnd 会设置成 ssthreshold/2, 而不是 1。而且不会走慢启动倍增的那种,而是走拥塞避免, 逐步+1 的那种。


Q: 前面“超时重传”的时候,是变成从 1 开始慢启动, 为什么这个“快重传”却是从 ssthreshold/2 开始,并且走拥塞避免? 为什么会有这个区别?

A:因为前面发生超时重传时, 是比较严重的情况, 超时时间内一个 ACK 都没收到。就好像来回数据都凭空消失了。而快速重传发生时, 还是能收到部分 ack 的, 只是丢失了部分数据, 说明拥塞没那么严重,于是可以大胆一点将 cwnd 削减到 1/4, 而不是直接从 1 开始。

其他

到了这里,基本就能理清楚超时重传和快重传的区别了,重点是理解这 2 个区别是怎么来的。后面再补几个问题,避免你和其他概念搞混,但不会说得太深,具体需要你自己去扩展学习了。

Q: 为啥发送地多了,数据就会部分丢失?这个是怎么个原理?

A:路由器有缓存,IP 分组接收过多时就会耗尽空间,丢弃数据。详细可以看路由器的数据转发原理。

Q: TCP 除了上面的重传定时器, 好象还有个坚持定时器?区别是啥?

A:坚持定时器和超时、网络拥塞没有关系, 和通告窗口即对端的接收能力有关。简单来说, 就是对方的传输层缓冲区(接收端窗口)满了,告诉你别发了,我吃不下了,于是返回通告窗口为 0。但你想知道啥时候可以发,于是就启动一个坚持定时器,每隔 5s 发送 1 个字节的小报文,小小地试探下。当通告窗口不为 0 了,就重新开始发。


点击关注,第一时间了解华为云新鲜技术~

发布于: 30 分钟前阅读数: 4
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
TCP的慢启动、拥塞避免、重传、快恢复乱七八糟总是记不清?11个连环问让你一次性打通任督二脉