原文:【圖解】你還在為 TCP 重傳、滑動窗口、流量控制、擁塞控制發愁嗎?看完圖解就不愁了
作者:小林coding
擁塞控制
為什么要有擁塞控制呀,不是有流量控制了嗎?
前面的流量控制是避免「發送方」的數據填滿「接收方」的緩存,但是並不知道網絡的中發生了什么。
一般來說,計算機網絡都處在一個共享的環境。因此也有可能會因為其他主機之間的通信使得網絡擁堵。
在網絡出現擁堵時,如果繼續發送大量數據包,可能會導致數據包時延、丟失等,這時 TCP 就會重傳數據,但是一重傳就會導致網絡的負擔更重,於是會導致更大的延遲以及更多的丟包,這個情況就會進入惡性循環被不斷地放大….
所以,TCP 不能忽略網絡上發生的事,它被設計成一個無私的協議,當網絡發送擁塞時,TCP 會自我犧牲,降低發送的數據量。
於是,就有了擁塞控制,控制的目的就是避免「發送方」的數據填滿整個網絡。
為了在「發送方」調節所要發送數據的量,定義了一個叫做「擁塞窗口」的概念。
什么是擁塞窗口?和發送窗口有什么關系呢?
擁塞窗口 cwnd是發送方維護的一個 的狀態變量,它會根據網絡的擁塞程度動態變化的。
我們在前面提到過發送窗口 swnd
和接收窗口 rwnd
是約等於的關系,那么由於入了擁塞窗口的概念后,此時發送窗口的值是swnd = min(cwnd, rwnd),也就是擁塞窗口和接收窗口中的最小值。
擁塞窗口 cwnd
變化的規則:
- 只要網絡中沒有出現擁塞,
cwnd
就會增大; - 但網絡中出現了擁塞,
cwnd
就減少;
那么怎么知道當前網絡是否出現了擁塞呢?
其實只要「發送方」沒有在規定時間內接收到 ACK 應答報文,也就是發生了超時重傳,就會認為網絡出現了用擁塞。
擁塞控制有哪些控制算法?
擁塞控制主要是四個算法:
- 慢啟動
- 擁塞避免
- 擁塞發生
- 快速恢復
慢啟動
TCP 在剛建立連接完成后,首先是有個慢啟動的過程,這個慢啟動的意思就是一點一點的提高發送數據包的數量,如果一上來就發大量的數據,這不是給網絡添堵嗎?
慢啟動的算法記住一個規則就行:當發送方每收到一個 ACK,就擁塞窗口 cwnd 的大小就會加 1。
這里假定擁塞窗口 cwnd
和發送窗口 swnd
相等,下面舉個栗子:
- 連接建立完成后,一開始初始化
cwnd = 1
,表示可以傳一個MSS
大小的數據。 - 當收到一個 ACK 確認應答后,cwnd 增加 1,於是一次能夠發送 2 個
- 當收到 2 個的 ACK 確認應答后, cwnd 增加 2,於是就可以比之前多發2 個,所以這一次能夠發送 4 個
- 當這 4 個的 ACK 確認到來的時候,每個確認 cwnd 增加 1, 4 個確認 cwnd 增加 4,於是就可以比之前多發 4 個,所以這一次能夠發送 8 個。
可以看出慢啟動算法,發包的個數是指數性的增長。
那慢啟動漲到什么時候是個頭呢?
有一個叫慢啟動門限 ssthresh
(slow start threshold)狀態變量。
- 當
cwnd
<ssthresh
時,使用慢啟動算法。 - 當
cwnd
>=ssthresh
時,就會使用「擁塞避免算法」。
擁塞避免算法
前面說道,當擁塞窗口 cwnd
「超過」慢啟動門限 ssthresh
就會進入擁塞避免算法。
一般來說 ssthresh
的大小是 65535
字節。
那么進入擁塞避免算法后,它的規則是:每當收到一個 ACK 時,cwnd 增加 1/cwnd。
接上前面的慢啟動的栗子,現假定 ssthresh
為 8
:
- 當 8 個 ACK 應答確認到來時,每個確認增加 1/8,8 個 ACK 確認 cwnd 一共增加 1,於是這一次能夠發送 9 個
MSS
大小的數據,變成了線性增長。
所以,我們可以發現,擁塞避免算法就是將原本慢啟動算法的指數增長變成了線性增長,還是增長階段,但是增長速度緩慢了一些。
就這么一直增長着后,網絡就會慢慢進入了擁塞的狀況了,於是就會出現丟包現象,這時就需要對丟失的數據包進行重傳。
當觸發了重傳機制,也就進入了「擁塞發生算法」。
擁塞發生
當網絡出現擁塞,也就是會發生數據包重傳,重傳機制主要有兩種:
- 超時重傳
- 快速重傳
這兩種使用的擁塞發送算法是不同的,接下來分別來說說。
發生超時重傳的擁塞發生算法
當發生了「超時重傳」,則就會使用擁塞發生算法。
這個時候,sshresh 和 cwnd 的值會發生變化:
ssthresh
設為cwnd/2
,cwnd
重置為1
接着,就重新開始慢啟動,慢啟動是會突然減少數據流的。這真是一旦「超時重傳」,馬上回到解放前。但是這種方式太激進了,反應也很強烈,會造成網絡卡頓。
就好像本來在秋名山高速漂移着,突然來個緊急剎車,輪胎受得了嗎。。。
發生快速重傳的擁塞發生算法
還有更好的方式,前面我們講過「快速重傳算法」。當接收方發現丟了一個中間包的時候,發送三次前一個包的 ACK,於是發送端就會快速地重傳,不必等待超時再重傳。
TCP 認為這種情況不嚴重,因為大部分沒丟,只丟了一小部分,則 ssthresh
和 cwnd
變化如下:
cwnd = cwnd/2
,也就是設置為原來的一半;ssthresh = cwnd
;- 進入快速恢復算法
快速恢復
快速重傳和快速恢復算法一般同時使用,快速恢復算法是認為,你還能收到 3 個重復 ACK 說明網絡也不那么糟糕,所以沒有必要像 RTO
超時那么強烈。
正如前面所說,進入快速恢復之前,cwnd
和 ssthresh
已被更新了:
cwnd = cwnd/2
,也就是設置為原來的一半;ssthresh = cwnd
;
然后,進入快速恢復算法如下:
- 擁塞窗口
cwnd = ssthresh + 3
( 3 的意思是確認有 3 個數據包被收到了) - 重傳丟失的數據包
- 如果再收到重復的 ACK,那么 cwnd 增加 1
- 如果收到新數據的 ACK 后,設置 cwnd 為 ssthresh,接着就進入了擁塞避免算法
也就是沒有像「超時重傳」一夜回到解放前,而是還在比較高的值,后續呈線性增長。
擁塞算法示意圖
好了,以上就是擁塞控制的全部內容了,看完后,你再來看下面這張圖片,每個過程我相信你都能明白: