簡介
擁塞指的是
在某段時間,若對網絡中某一資源的需求超過了該資源所能提供的可用部分,網絡性能就要變壞。這種情況就叫擁塞(congestion)。
TCP模塊任務:提供網絡利用率,降低丟包率,保證網絡資源對每條數據的公平性。---- 擁塞控制
標准文檔:RFC 5681,介紹了擁塞控制4個部分:慢啟動(slow start)、擁塞避免(congestion avoidance)、快速重傳(fast retransmit)和快恢復(fast recovery)。
擁塞控制for linux算法實現,有多種:reno算法、vegas算法、cubic算法等。部分或全部實現。
查看方式(for Ubuntu 14):
$ cat /proc/sys/net/ipv4/tcp_congestion_control
cubic
我的PC是采用cubic算法。
術語介紹
擁塞控制最終受控變量:發送窗口(SWND,Send Window)。也就是說,擁塞控制算法最終是通過控制SWND大小,來進行擁塞控制的。
發送窗口
在發送端發送緩存,存在4種類型數據,其中,發送窗口SWND是指(2)和(3)
(1)已經發送並且對端確認(Sent/ACKed)---------------發送窗外 緩沖區外
(2)已經發送但未收到確認數據(Sent/UnACKed)----------發送窗內 緩沖區內
(3)允許發送但尚未發送的數據(Unsent/Inside)---------發送窗內 緩沖區內
(4)未發送暫不允許(Unsent/Outside)-----------------發送窗外 緩沖區內
發送緩存4種類別數據的示意圖
接收窗口
接收窗口 指的是TCP報文頭窗口字段,用於告訴發送端自己當前接收緩存大小。
發送者最大段大小 SMSS
MSS(Maximum Segment Size) TCP最大報文段長度(RFC 879),指的是每一個TCP報文段中的數據字段的最大長度。
TCP報文段 = 數據字段 + TCP首部(20~40Byte)
MSS = MTU - sizeof(TCPHDR) + sizeof(IPHDR)
MTU:最大傳輸單元(含TCP頭部、IP頭部);TCPHDR:TCP報文頭;IPHDR:IP報文頭。
MSS與MTU含義見下圖:
發送者最大段大小,稱為SMSS(Sender Maximum Segment Size)。
如何通告MSS?
MSS的默認值是536byte,因此對端應能接受報文段長度是536 + 20(TCP固定首部) = 556byte的TCP報文。如果想要改變MSS,就需要在TCP連接時,通過TCP報文頭可變長的選項(option)字段(1byte類型+1byte長度+不定長內容),告知對端。
注意:MSS是設置好值后,再通告對方,而非與對方協商。
SWND大小的影響
如果SWND太小,會引起明顯的網絡延遲;反之,如果SWND太大,則容易導致網絡擁塞。
既然接收窗口RWND可以控制發送窗口,為何還需要擁塞控制,而不直接用接收窗口進行控制?
雖然接收方可以通過接收通告窗口(RWND),來控制發送端SWND。但是接收方並不知道網絡擁塞情況,無法針對網絡情況進行控制。因此,發送端引入了一個稱為擁塞窗口的(Congestion Windo, CWND)的狀態變量。實際的SWND值是RWND和CWND中較小者。
慢啟動和擁塞避免
慢啟動
TCP連接建立OK后,CWND初值IW(Initial Window),大小2~4SMSS -- 發送端最多能發送IW bytes數據。
此后,發送端每收到接收端的一個確認,其CWND就按下式增加:
CWND += min(N, SMSS), 其中,N是此次確認中包含的之前未被確認的字節數 (1)
這樣,CWND將按指數形式擴大,這就是慢啟動。
慢啟動算法理由:TCP模塊剛開始發送數據時,並不知道網絡的實際情況,需要一種試探的方式平滑地增加CWND的大小。
慢啟動門限
如果不施加其他手段,慢啟動必然使得CWND很快膨脹(慢啟動並不慢),並最終導致網絡擁塞。而慢啟動門限(slow start threadhold size, ssthresh),就是這樣一個限制:
當cwnd < ssthresh時,使用慢開始算法;
當cwnd > ssthresh時,停止慢開始算法,改用擁塞避免算法;
當cwnd = ssthresh時,既可以用慢開始算法,也可以用擁塞避免算法;
ssthresh初始值16。只要判斷出現擁塞時,慢開始門限設為出現擁塞時發送窗口swnd的一半(但不能<2),然后把擁塞窗口cwnd重新設為1,執行慢開始算法。
目的:迅速減少主機發送到網絡中的分組數,使得發生擁塞時路由器有足夠時間把隊列中積壓的分組處理完畢。
擁塞避免算法
CWND按線性方式增加,從而減緩其擴大。RFC 5681提到2種實現方式:
1)每個RTT時間內,按(1)式計算新的CWND,而不論該RTT時間內發送端收到多少個確認。
2)每收到一對新數據的確認報文段,就按式(2)來更新CWND。
CWND += SMSS * SMSS / CWND (2)
下圖粗略地描述了慢啟動和擁塞避免發生的時機和區別。此外,我們假設當前ssthresh = 16SMSS大小(實際遠不止這么大)
慢啟動和擁塞避免,都是發送端在未檢測到擁塞時,所采用的積極擁塞避免的方法。下面介紹擁塞發生時(可能發生在慢啟動階段或擁塞避免階段)擁塞控制的行為。
發送端如何判斷擁塞發生?
- 傳輸超時,或者說TCP重傳定時器溢出; ---- 擁塞避免
- 接收到連續3個重復的確認報文; ---- 快速重傳,快速恢復
擁塞控制對這兩種情況有不同的處理方式。第一種情況,仍然使用慢啟動和擁塞避免;第二種情況,則使用快速重傳和快速回復(如果真的發生擁塞)。
如果發送端檢測到擁塞發生是由於傳輸超時,即上述第一種情況,那么它將執行重傳並做如下調整:
ssthresh = max(FlightSize / 2, 2 * SMSS) (3)
CWMD <= SMSS
其中,FlightSize是已經發送但未收到確認的字節數。這樣調整后,CWMD將小於SMSS,那么必然小於新的慢啟動門限值ssthresh(根據式(3),ssthresh一定不小於SMSS的2倍),故而擁塞控制再次進入慢啟動階段。
何為TCP超時重傳?
TCP服務必須能夠重傳超時時間內未收到確認TCP報文段。為此,TCP模塊為每個TCP報文段都維護一個重傳定時器,該定時器在TCP報文段第一次被發送時啟動。如果超時時間內未收到對方的應答,TCP模塊將重傳TCP報文段並重置定時器。
所謂超時,是指定時器 > 最大往返時間RTT。
RTT是指一個數據報發送到目的地,然后到發送方收到確認所需的時間,這是測量RTT。發送報文和確認報文並非數量上的一一對應,可能發送多次,確認一次,也可能是一一對應。
快速重傳和快速恢復
如何通過接收到重復的確認報文段,判斷網絡是否真的發生擁塞?
發送端接收到重復的確認報文段可能情形:TCP報文段丟失,接收端收到亂序TCP報文段並重排等。
擁塞控制算法需要判斷當收到重復的確認報文段時,網絡是否真的發生了擁塞,或者說TCP報文段是否真的丟失了。具體做法:
發送端如果連續收到3個重復的確認報文段,就認為擁塞發生了。然后它啟用快速重傳和快速恢復算法來處理擁塞,過程如下:
1)當收到第3個重復的確認報文段時,按(3)式計算ssthresh,然后立即重傳丟失的報文,而不是等到重傳計時器超時,這稱為快速重傳。並按式(4)設置CWND
CWND = ssthresh + 3 * SMSS (4)
2)每次收到1個重復的確認時,設置CWND = CWND + SMSS,而非從慢開始算法的cwnd=1開始。此時,發送端可以發生新的TCP報文段(如果新的CWND允許的話)
3)當收到新數據的確認時,設置CWND = ssthresh(ssthresh是新的慢啟動門限值,由第一步計算得到)
步驟1)稱為快速重傳,步驟2)3)稱為快速恢復。快速重傳和快速恢復完成之后,擁塞控制將恢復到擁塞避免階段。這點由第3)步操作可得知。
總結
擁塞控制對象:發送窗口SWND. SWND <= min(RWND, CWND)
網絡出現超時 => 采用慢開始算法 + 擁塞避免算法;
網絡出現重傳 => 快速恢復算法;
參考
[1]謝希仁. 計算機網絡.第5版[M]. 電子工業出版社, 2008.
[2]游雙. Linux高性能服務器編程[M]. 機械工業出版社, 2013.
[3]Postel J . Transmission control protocol; rfc793[J]. Rfc, 1981.(RFC793)
[4]Тезисы, Статусдокумента, Авторскиеправа, et al. RFC 5681 TCP Congestion Control. 1996.(RFC5681)