壅塞與流量控制

壅塞避免(congestion avoidance)

一旦你在那間暗房裡大致摸到牆在哪,你就不再把步伐加倍,而是改成一點一點往前挪——一次一個謹慎的小步,因為現在踏錯一步就會撞痛小腿。壅塞避免(congestion avoidance)就是 TCP 謹慎踏步的階段:在慢啟動迅速找到網路容量的大致範圍之後,TCP 切換成緩慢、線性地增大視窗,溫和地試探是否還能多榨出一點頻寬,而不會一頭撞進塞車。

這個切換發生在壅塞視窗達到慢啟動門檻(ssthresh)的時候。從此之後,TCP 不再每個往返把視窗加倍,而是每個往返只加上大約一個封包份——這就是 AIMD 中加法增大的那一半。這是緩慢的線性攀升。當遺失終於發出壅塞信號,TCP 執行乘法減小(傳統 TCP 把視窗砍半),並把 ssthresh 設為新的、較低的視窗值,這樣下次它就大致知道天花板在哪。反覆下來的結果,就是 TCP 鋸齒:一次急遽的慢啟動爬升,接著是繞著真實容量永無止境的溫和爬升與砍半。

這兩個階段把工作分得很乾淨。慢啟動的存在,是為了從靜止起步快速找到正確的範圍;壅塞避免的存在,則是為了可持續地待在那裡,隨著容量緩緩漂移(其他流來來去去)持續追蹤它,又不引發激進試探會造成的嚴重遺失。一條長壽連線的大部分時間——一次大檔傳輸、一段長影片串流——都花在壅塞避免裡,安靜地在連結極限下方一點點地鋸齒擺動。

慢啟動在 ssthresh = 64 個封包處結束後,由壅塞避免接手:視窗成長為 64、65、66、67……每個往返加一個封包。到 80 時發生遺失;TCP 砍半到 40 並把 ssthresh 設為 40,接著再線性爬升 40、41、42……。相較之下,慢啟動本來會一步就加倍到 128。

在 ssthresh 之上,TCP 每個 RTT 只加一個封包——這緩慢的線性試探,讓穩定的連線停留在容量附近。

在非常快、長距離的連結上,線性的加法增大慢得令人痛苦:在一次遺失之後,單靠每個 RTT+1 個封包的攀升,可能要花上好幾分鐘才能把高頻寬延遲乘積的路徑重新填滿。正是這種遲鈍,使得 CUBIC(其視窗依三次方曲線成長)取代了單純的線性成長,成為 Linux 的預設。

又称
linear-growth phase壅塞避免擁塞避免