淺談TCP/IP四種計時器、慢啟動、擁塞避免、快速重傳、快速恢復
持續計時器
假設一種場景:A給B傳送資料,如果B告訴A自己的緩衝區已滿,於是A停止傳送資料,等待一段時間後,B的緩衝區出現了富餘,於是給A傳送報文告訴rwnd大小為400,但是這個報文不幸丟失了,於是就出現A等待B的通知,B等待A傳送資料的死鎖狀態,為了處理這種問題,TCP引入了持續計時器,當A收到B的零視窗通知時,就啟用該計時器,時間到則傳送一個位元組的探測報文,對方會在此時迴應自身的接收視窗大小,如果仍然為0,則重置持續計時器,繼續等待.
重傳計時器
當TCP傳送報文段時,就建立該特定報文段的重傳計時器,可能發-生兩種情況:
- 若在計時器截止時間到之前收到了對此特定報文段的確認,則撤銷此計時器.
- 若收到了對此特定報文段的確認之前計時器截止期到,則重傳此報文段,並將計時器復位.
保活計時器
保活計時器使用在某些實現中,用來防止在兩個TCP之間的連接出現長期的空閒,假定客戶端打開了伺服器的連線,傳送了一些資料,然後就保持靜默,也許這個客戶端出故障了,在這種情況下,這個連線將永遠保持開啟狀態,要解決這種問題,在大多數的實現中都是使伺服器設定保活計時器,每當伺服器收到客戶端的資訊,就將計時器復位,超時通常設定為2小時,若伺服器過了2小時還沒有收到客戶端的資訊,就傳送探測報文段,若傳送10個探測報文段(每一個相隔75秒)還沒有響應,就假定客戶端除了故障,因而就終止該連線.
時間等待計時器
慢啟動
最初的TCP在連線建立成功後會向網路中傳送大量的資料包,這樣很容易導致網路中路由器快取空間耗盡,從而發生擁塞。因此新建立的連線不能夠一開始就大量傳送資料包,而只能根據網路情況逐步增加每次傳送的資料量,以避免上述現象的發生。具體來說,當新建連線時,cwnd初始化為1個最大報文段(MSS)大小,傳送端開始按照擁塞視窗大小發送資料,每當有一個報文段被確認,cwnd就增加1個MSS大小。這樣cwnd的值就隨著網路往返時間(Round Trip Time,RTT)呈指數級增長,事實上,慢啟動的速度一點也不慢,只是它的起點比較低一點而已。我們可以簡單計算下:
開始 ——> cwnd = 1
經過一個RTT 後(即收到一個ACK後) cwnd = 2*1 = 2
經過兩個RTT後(即收到兩個ACK後) cwnd = 2*2 =4
經過三個RTT後(即收到兩個ACK後) cwnd =2*2*2 = 8
擁塞避免
從慢啟動可以看到,cwnd可以很快的增長上來,從而最大程度的利用網路頻寬資源,但是cwnd不能一直這樣無限增長下去,一定需要某個限制。TCP使用了一個叫慢啟動門限(ssthresh)的變數,當cwnd超過該值後,慢啟動過程結束,進入擁塞避免階段。對於大多數TCP實現來說,ssthresh的值是65536(同樣以位元組計算)。擁塞避免的主要思想是加法增大,也就是cwnd的值不再指數級往上升,開始加法增加。此時當視窗中所有的報文段都被確認時,cwnd的大小加1,cwnd的值就隨著RTT開始線性增加,這樣就可以避免增長過快導致網路擁塞,慢慢的增加調整到網路的最佳值。
上面討論的兩個機制都是沒有檢測到擁塞的情況下的行為,那麼當發現擁塞了cwnd又該怎樣去調整呢?
首先來看TCP是如何確定網路進入了擁塞狀態的,TCP認為網路擁塞的主要依據是它重傳了一個報文段。上面提到過,TCP對每一個報文段都有一個定時器,稱為重傳定時器(RTO),當RTO超時且還沒有得到資料確認,那麼TCP就會對該報文段進行重傳,當發生超時時,那麼出現擁塞的可能性就很大,某個報文段可能在網路中某處丟失,並且後續的報文段也沒有了訊息,在這種情況下,TCP反應比較“強烈”:
1.把ssthresh 降為cwnd值的一半.
2.把cwnd重新置為1
3.重新進入慢啟動過程。
從整體上來講,TCP擁塞控制視窗變化的原則是AIMD原則,即加法增大、乘法減小。可以看出TCP的該原則可以較好地保證流之間的公平性,因為一旦出現丟包,那麼立即減半退避,可以給其他新建的流留有足夠的空間,從而保證整個的公平性。
快速重傳
其實TCP還有一種情況會進行重傳:那就是收到3個相同的ACK。TCP在收到亂序到達包時就會立即傳送ACK,TCP利用3個相同的ACK來判定資料包的丟失,此時進行快速重傳,快速重傳做的事情有:
1.把ssthresh設定為cwnd的一半
2.把cwnd再設定為ssthresh的值+3
3.重新進入擁塞避免階段
快速恢復
後來的“快速恢復”演算法是在上述的“快速重傳”演算法後新增的,當收到3個重複ACK時,TCP最後進入的不是擁塞避免階段,而是快速恢復階段。快速重傳和快速恢復演算法一般同時使用。快速恢復的思想是“資料包守恆”原則,即同一個時刻在網路中的資料包數量是恆定的,只有當“老”資料包離開了網路後,才能向網路中傳送一個“新”的資料包,如果傳送方收到一個重複的ACK,那麼根據TCP的ACK機制就表明有一個數據包離開了網路,於是cwnd加1。如果能夠嚴格按照該原則那麼網路中很少會發生擁塞,事實上擁塞控制的目的也就在修正違反該原則的地方。
1.當收到3個重複ACK時,把ssthresh設定為cwnd的一半,把cwnd設定為ssthresh的值加3,然後重傳丟失的報文段,加3的原因是因為收到3個重複的ACK,表明有3個“老”的資料包離開了網路。
2.再收到重複的ACK時,擁塞視窗增加1。
3.當收到新的資料包的ACK時,把cwnd設定為第一步中的ssthresh的值。原因是因為該ACK確認了新的資料,說明從重複ACK時的資料都已收到,該恢復過程已經結束,可以回到恢復之前的狀態了,也即再次進入擁塞避免狀態。