參考文獻: RFC2581, RFC3390
1. 前言
TCP是具備流控和可靠連接能力的協議,為防止TCP發生擁塞或為提高傳輸效率,在網
絡發展早期就提出了一些相關的TCP流控和優化算法,而且也被RFC2581規定是每個
TCP實現時要實現的。
本文中,為求方便把將“TCP分組段(segment)”都直接稱為“包”。
2. 慢啟動(slow start)和擁塞避免(Congestion Avoidance)
慢啟動和擁塞避免是屬于TCP發送方必須(MUST)要實現的,防止TCP發送方向網絡傳入大量的突發數據造成網絡阻塞。
先介紹幾個相關參數,是在通信雙方中需要考慮但不在TCP包中體現的一些參數:
擁塞窗口(congestion window,cwnd),是指發送方在接收到對方的ACK確認前向允許網絡發送的數據量,數據發送后,擁塞窗口縮小;接收到對方的ACK后,擁塞窗口相應增加,擁塞窗口越大,可發送的數據量越大。擁塞窗口初始值的RFC2581中被規定為不超過發送方MSS的兩倍,而且不能超過兩個TCP包,在RFC3390中更新了初始窗口大小的設置方法。
通告窗口(advertised window,rwnd),是指接收方所能接收的沒來得及發ACK確認的數據量,接收方數據接收后,通告窗口縮小;發送ACK后,通告窗口相應擴大。
慢啟動閾值(slow start threshold, ssthresh),用來判斷是否要使用慢啟動或擁塞避免算法來控制流量的一個參數,也是隨通信過程不斷變化的。
當cwnd < ssthresh時,擁塞窗口值已經比較小了,表示未經確認的數據量增大,需要啟動慢啟動算法;當cwnd > ssthresh時,可發送數據量大,需要啟動擁塞避免算法。
擁塞窗口cwnd是根據發送的數據量自動減小的,但擴大就需要根據對方的接收情況進行擴大,慢啟動和擁塞避免算法都是描述如何擴大該值的。
在啟動慢啟動算法時,TCP發送方接收到對方的ACK后擁塞窗口最多每次增加一個發送方MSS字節的數值,當擁塞窗口超過sshresh后或觀察到擁塞才停止算法。
啟動擁塞避免算法時,擁塞窗口在一個連接往返時間RTT內增加一個最大TCP包長度的量,一般實現時用以下公式計算:
cwnd += max(SMSS*SMSS/cwnd, 1) (2.1)
SMSS為發送方MSS。
TCP發送方檢測到數據包丟失時,需要調整ssthresh,一般按下面公式計算:
ssthresh = max (FlightSize / 2, 2*SMSS) (2.2)
其中FlightSize表示已經發送但還沒有被確認的數據量。
3. 快速重傳(fast retransmit)和快速恢復(fast recovery)
TCP接收方收到錯序的TCP包時要發送復制的ACK包回應,提示發送方可能出現網絡丟包;發送方
收到連續3個重復的ACK包后啟動快速重傳算法,根據確認號快速重傳那個可能丟失的包而不必等
重傳定時器超時后再重傳,普通的重傳是要等到重傳定時器超時還沒收到ACK才進行的。這個算
法是TCP發送方應該(SHOULD)實現的,不是必須。TCP發送方進行了快速重傳后進入快速恢復階段
,直到沒再接收重復的ACK包。
快速重傳和快速恢復具體過程為:
1. 當收到第3個重復的ACK包時,ssthreh值按公式2.2重新設置;
2. 重傳丟失的包后,將擁塞窗口cwnd設置為sshresh+3*SMSS,人工擴大了擁塞窗口;
3. 對于每個接收到的重復的ACK包,cwnd相應增加SMSS,擴大擁塞窗口;
4. 如果新的擁塞窗口cwnd值和接收方的通告窗口值允許的話,可以繼續發新包;
5. 當收到下一個ACK確認了新數據時,將cwnd大小調整為sshresh,減少窗口;對接收方
來說,接收到重發的TCP包后就要發此ACK確認當前接收的數據。
4. 結論
這些算法重點在于保持網絡的可靠性和可用性,防止網絡阻塞造成的網絡崩潰,是相對
比較保守的。
5. 附錄討論
A君: 這些算法都是針對通信雙方的事, 但如果從開發防火墻等中間設備的角度來看,
中間設備有必要考慮這些么?
端木: 這個...我好象也看不出必要性,因為算法的參數都是在雙方內部而不在TCP數據包
中體現...但應該會讓中間設備輕松點,這個就象在馬路開車,這些算法就是交規
讓你開得規矩點,交警只關心你開車的情況,而不管你開的是什么車,開得好交警
也輕松。好車可以讓你很容易開好,但差車也可以開好。
A君: 這些算法原型提出也很早了, 最早是88年的事, 當時網絡都處于初級階段, 有個
9600bps的貓就很牛了, 計算機性能也很差, 因此實施這些算法還有點用; 但現
在過了快20年了, 百兆都快淘汰, 千兆, 萬兆網絡都快普及了, 即使PC機的內存
也都上G了,再規矩這種幾K級別的數據量有意思么? 就好象現在噴氣式戰斗機都到
第4代了, 再研究螺旋槳戰斗機還有意思么?
端木: 這個...這個就象病毒庫了, 里面不也有無數的DOS時代的病毒, 你以后這輩子估計
都見不著的,但沒有哪個防病毒廠商會把這些病毒從庫中剔除,庫是只增不減的。
有這么個東西也是一樣,正因為平時沒用,誰也不注意,知道了就可以吹一吹,
尤其拿去唬唬人是很有效的!
A君: 你真無聊!
端木: You got it! 不無聊干嗎寫博客啊!
端木: 搞技術有時候是很悲哀的一件事,必須牽扯七大姑八大姨的很多老東西,也就是向下
兼容,到一定程度將成為進一步發展的最大障礙,講一個從smth看到的不是笑話
的笑話:
現代鐵路的鐵軌間距是4英尺8點5英寸,鐵軌間距采用了電車輪距的標準,而電車輪距
的標準則沿襲了馬車的輪距標準。
馬車的輪距為何是4英尺8點5英寸?原來,英國的馬路轍跡的寬度是4英尺8點5英寸。
如果馬車改用其他尺寸的輪距,輪子很快就會在英國的老馬路上撞壞。
英國馬路的轍跡寬度又從何而來?這可以上溯到古羅馬時期。整個歐洲(包括英國)的老路都是羅馬人為其軍隊鋪設的,4英尺8點5英寸正是羅馬戰車的寬度。
羅馬戰車的寬度又是怎么來的?答案很簡單,它是牽引一輛戰車的兩匹馬的屁股的總寬度。
段子到這里還沒有結束。美國航天飛機的火箭助推器也擺脫不了馬屁股的糾纏———火箭助推器造好之后要經過鐵路運送,而鐵路上必然有一些隧道,隧道的寬度又是根據鐵軌的寬度而來。代表著尖端科技的火箭助推器的寬度,竟然被兩匹馬的屁股的總寬度決定了。