【TCP】拥塞控制

由于 TCP 协议向应用层提供不定长的字节流发送方法,使得 TCP 协议先天性的就有意愿去占满网络中的整个带宽。但是当网络中许多 TCP 连接同时试图去占满整个带宽的时候,就有可能发送恶行拥塞事件。

TCP 的拥塞控制算法能够有效的降低网络中的拥塞,提升所有 TCP 连接的发送速度。

慢启动

拥塞窗口 cwnd (congestion window)

在TCP中,拥塞窗口(congestion window)是任何时刻内确定能被发送出去的字节数的控制因素之一,是阻止发送方至接收方之间的链路变得拥塞的手段。

  • 通告窗口 rwnd (receiver`s advertised window)
  • 发送窗口 swnd = min (cwnd, rwnd)

每收到一个 ACK,cwnd 扩充一倍

image

图中初始拥塞窗口是 1 个 MSS (Maximum Segment Size,最大报文段长度),实际上当下慢启动的初始拥塞窗口是 10 个 MSS。

慢启动算法将提高传输速率,直到检测到接收不到 ACK 。如果发生丢包事件,TCP 将假定这是由于网络拥塞,并采取措施减少网络上的负载。

慢启动的初始窗口

慢启动初始窗口 IW(Initial Window)的变迁

  • 1 MSS: RFC2001 (1997)

  • 2-4 MSS: RFC2414 (1998)

    • IW = min(4*MSS,max(2*MSS,4380 bytes))
  • 10 MSS: RFC6928 (2013)

    • IW = min(10*MSS,max(2*MSS,14600 bytes))

初始窗口在 2-4 MSS后,谷歌发现互联网中的网页基本上都在 10 个 segments 左右,这个时候如果还采用初始的 3 个 MSS 会使得浪费 3 个 RTT(往返时间) 去传递最初的扩充信息,所以谷歌建议使用 IW=10 。

实际上慢启动要个拥塞避免放在一起使用的。

拥塞避免

慢启动算法是以指数级增加拥塞窗口的,所以当出现丢包时丢包的数量一定会非常大,拥塞避免则可以很好的解决这一问题。

慢启动阈值 ssthresh(slow start threshold)

达到 ssthresh 后,以线性方式增加 cwnd。计算公式为: cwnd += MSS*MSS/cwnd

image

如图所示,刚开始 cwnd=4*MSS,ssthresh无限制,在 cwnd 达到 64 时丢包,将 ssthresh置为32,cwnd 重新以慢启动算法增加,以此循环往复。

快速重传 && 快速恢复

现在我们已经可以知道,当出现丢包时将重新执行慢启动。此时意味着拥塞窗口大幅度下降,发送速率也会大幅度下降。

当丢包出现的场景并不是很严重的时候,我们其实可以采用快速重传与快速恢复。

失序数据段

image

产生失序数据段的原因:

  • 若报文丢失,将会产生连续的失序 ACK 段

  • 若网络路径与设备导致数据段失序,将会产生少量的失序ACK段

  • 若报文重复,将会产生少量的失序ACK段

快速重传

image

接收方

  • 当接收到一个失序数据段时,立刻发送它所期待的缺口 ACK 序列号;如: 收到Pkt6立刻返回 ACK5

  • 当接收到填充失序缺口的数据段时,立刻发送它所期待的下一个ACK序列号;如: 收到 Re-Pkt5 立刻返回 ACK9

发送方

  • 当接收到3个重复的失序ACK段(4个相同的失序ACK段)时,不再等待重传定时器的触发,立刻基于快速重传机制重发报文段;如: 接收到三个重复的 ACK5,立刻重发 Re-Pkt5

快速重传意味着出现丢包,出现丢包一定要进入慢启动吗?

快速重传下整个网络还是通的,还在频繁收到重复 ACK,意味着网络仍在流动。因此没有必要进入慢启动。

慢启动会突然减少数据流

快速恢复(RFC2581)

快速恢复就是针对在快速重传这种丢包场景下,我们不需要重新进入慢启动,而只是适当的把网速降下来一点。

image

启动快速重传且正常未失序 ACK 段到达前,启动快速恢复。快速恢复的步骤如下:

  • 将 ssthresh 设置为当前拥塞窗口 cwnd 的一半,设当前cwnd为 ssthresh 加上 3*MSS

  • 每收到一个重复 ACK , cwnd 增加 1 个MSS

  • 当新数据 ACK 到达后,设置 cwnd 为 ssthresh (如:上面的 pkt9 的 ACK9)

在快速重传场景中,当 报文5 丢失,报文6,7,8 被接收端接收到以后。发送端收到了连续的确认 报文5 的 ACK,那么此时发送端到底只是重发 报文5 还是要重发 报文5,6,7,8 呢。

接下来要介绍的选择性重传算法,将要解决这一问题。

选择性重传算法

由于 TCP 的序列号采用累积确认的方式,所以像上面遇到的场景:接收方未收到 报文5,但收到了 报文6,7,8。

它只会反复的给发送方说“我还需要报文5”,这样发送方只知道报文5没有发过去,并不知道 报文6,7,8 有没有发过去。

所以发送方要么采用积极的方式重传 报文5,6,7,8,要么采用乐观的方式只重传 报文5

  • 重传所有段:积极悲观

    • 可能浪费带宽
  • 仅重传丢失段:保守乐观

    • 大量丢失包时效率低下

为了解决保守乐观和积极悲观各自得问题。TCP 引入了 SACK(选择性确认)

SACK: TCP Selective Acknowledgment

  • RFC2018

image

SACK - 选择性确认过程如下图:

image

当 server 连续发送四个报文,而 client 先收到了第四个报文,没有收到第三个报文的时候,client 就会在 ACK 确认中对于第三个报文我没有收到,但是加了个 SACK 说361-500 也就是第四个报文的这段序列号我收到了。这时候 server 就可以针对性的知道第四个报文已经收到了,我们只需要发第三个报文。

所以 server 不用陷入,到底时选择积极悲观还是保守乐观的策略。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇