小村軟體有限公司
第 5 章

壅塞控制 — TCP 向網路的壅塞退讓

路徑有多擠沒有人會告訴你。TCP 把自備的窗 cwnd 成倍撐大,並以封包遺失為訊號退讓 — 用手算追蹤這套邊試探邊傳送的機制。

對方很健康,路卻很擠

有了第 4 章的流量控制,再也不會把對方淹死了。那麼,只要 rwnd 允許就全力送出可以嗎?答案是否定的。送信端與接收端之間,是別人的通訊也會流進來的共用網路。超過途中路由器處理能力的部分會積在佇列裡,佇列溢出就會被丟棄。所有人都全力送的話,重傳會招來更多重傳,整個網路堵死、誰的資料都流不動(壅塞崩潰 — 1980 年代實際發生過的事故)。

麻煩的是,路徑的剩餘容量沒有人會告訴你。不存在像 rwnd 那樣的申告。於是 TCP 作為承諾之 3 的後半,擁有由送信端自力推定路徑容量的壅塞控制。推定值以另一扇窗 — 壅塞視窗(cwnd) — 的形式持有,實際可以送出的量由 min(rwnd, cwnd) 決定。對方的狀況與路的狀況,配合較嚴格的一方。

cwnd 隨時間以慢啟動指數增加,過了門檻值後轉為每次加 1 的直線增長,因偵測到遺失而掉到一半或 1 再重新爬升的曲線圖風格示意圖

cwnd 的一生。以倍增衝刺(慢啟動),過了門檻值就一步一步(壅塞避免),因遺失而退讓再重新爬。鋸齒狀的波形是 TCP 健全地持續試探極限的樣子。

試探的方式 — 倍速衝刺,再一步步逼近

在不知道容量的路上,只能從少量開始、邊觀察邊增加。TCP 的試探是 2 段變速。

慢啟動 — 每 RTT 倍增
從 cwnd = 1 MSS(1 個區段份)開始,每當 ACK 平安回來就增加。結果是 1 → 2 → 4 → 8 → 16… 每 1 RTT 翻倍。與名字相反,是指數式的急加速
壅塞避免 — 每 RTT +1
超過門檻值(ssthresh:上次發生遺失水準的記憶)後切換成加法,每 1 RTT 加 1 MSS。在極限附近輕輕地試探

然後,一旦越過路徑的極限就會發生封包遺失,第 3 章的偵測開始運作。這裡依證據的重量改變懲罰正是 TCP 的智慧。

重複 ACK 3 次(輕症)
後續有送達 = 路還活著。把 cwnd 減到約一半,直接以壅塞避免繼續
逾時(重症)
什麼都沒回來 = 嚴重的堵塞。退回 cwnd = 1 MSS,從慢啟動重新開始

增加、撞上、退讓、再增加。cwnd 曲線描出的鋸齒狀波形,就像是 TCP 為了讓所有人分享網路容量而持續支付的問候。如果是誰都不退讓的設計,網際網路是不可能成立的。

練習 5-1 — 區分 2 種煞車

練習用「配合的是誰的狀況」來說清楚 2 扇窗的差別。

Q1. 關於流量控制與壅塞控制的差異,正確的說明是哪一個?

Q2. TCP 把封包遺失當作「壅塞的訊號」使用,是為什麼?

Q3. 決定送信端在某個瞬間可以送出的資料量(in-flight 的上限)的是哪一個?

在實務上會怎麼呈現

壅塞控制平常是透明的,但在效能問題上必然現身。

  • 剛連上時誰都很慢 — 不管線路多粗,都是從 cwnd = 1 MSS 附近開始試探,所以短的通訊一次都達不到最高速。與其大量重新建立小請求的連線,不如重複使用 1 條連線(keep-alive)更快,理由就在這裡。
  • 1 次遺失的成本不是「重傳 1 個」就了事 — 因為 cwnd 會掉到一半(或 1),吞吐量的恢復要花上好幾個 RTT。遺失率 1% 不是「慢 1%」,而是可能慢上好幾個數量級。
  • 速度碰頂有 3 個嫌疑人 — rwnd(對方的緩衝區)、cwnd(路徑的壅塞),以及頻寬本身。套進第 4 章的式子,思考是哪扇窗取了 min,就是切分問題的定式。

練習 5-2 — 手算 cwnd 的成長

cwnd 以區段數(MSS 為單位)來數。請計算倍增與加法這 2 種成長。

Q4. 以慢啟動從 cwnd = 1 MSS 開始傳送。當 cwnd 每 1 RTT 變成 2 倍時,經過 3 個 RTT 後的 cwnd 是多少 MSS?

MSS

Q5. 超過門檻值(ssthresh)進入壅塞避免模式,從 cwnd = 10 MSS 起每 1 RTT 增加 1 MSS。經過 5 個 RTT 後的 cwnd 是多少 MSS?

MSS

Q6. TCP 偵測到遺失時的反應,依偵測方式而不同。正確的組合是哪一個?(請回想第 3 章的 2 種偵測方法)

本章帶走的重點

  • 流量控制配合對方的狀況,壅塞控制配合網路的狀況。可以送的量是 min(rwnd, cwnd)
  • 路徑的容量沒有人申告,所以以封包遺失為壅塞的訊號自力試探
  • 成長是 2 段變速 — 慢啟動每 RTT 倍增,壅塞避免每 RTT +1 MSS
  • 懲罰也是 2 段 — 重複 ACK 3 次減半,逾時退回 1 重新開始。鋸齒狀的波形是健全互讓的樣子

至此 3 項承諾全部到齊。下一章把視角移到應用程式端。TCP 明明完美地守著承諾,應用程式的資料看起來卻「缺漏、黏在一起」 — 實務上最常見的 TCP 誤解,位元組串流與 framing 的故事。