TCP協議學習總結(下)
在前兩邊TCP學習總結中,也大概地學習了TCP的整個流程,但許多細節中的細節並沒有詳細學習,例如超時重傳問題,每次瓶頸迴歸慢啟動效率問題以及最大視窗限制問題等。本學習篇章最要針對這些細節中的細節進行學習。TCP的複雜很多時候就是細節太多了,需要考慮許多的場景並利用許多複雜的演算法和啟動非同步執行緒定時處理這些問題,對於每一個連線,TCP管理4個不同的定時器,分別是:
1)、重傳定時器使用於當希望收到另一端的確認;
2)、堅持定(persist)時器使視窗大小資訊保持不斷流動,即使另一端關閉了其接收視窗;
3)、保活(keepalive)定時器可檢測到一個空閒連線的另一端何時崩潰或重啟;
4)、2MSL定時器測量一個連線處於TIME_WAIT狀態的時間。
TCP的超時與重傳
RTO(Retransmission TimeOut)演算法
TCP計算超時有一個叫做“指數退避”的演算法,例如如果傳送方在傳送資料的時候,服務端突然關閉服務時,客戶端是會根據初始超時時間開始一步步指數的增長超時重傳時間,如下圖所示:
以上簡單例子的第一次超時重傳時間是1.5秒,第二次是3秒,第三次是6秒以此指數增長類推,直到最大超時時間64秒後就不再增長,直到9分鐘(TCP實現中不可變)後客戶端直接傳送一個RST報文段斷開連線。由於路由器和網路流量均會變化,因此TCP會跟蹤這些變化並相應地改變其超時時間,所以超時的初始化時間不是固定的。它會根據報文段的往返時間(RTT)去評估,具體演算法如下:
最初演算法:R=αR+(1-α)M ;RTO=Rβ;
R:RTT估計器;
M:當前測量值;
α:是一個推薦值為0.9的平滑因子;
β:是一個推薦值為2的時延離散因子。
從以上演算法可以看出,每個新估計的90%來自於前一個估計,而10%則取自新的測量。[Jacobson 1988]詳細分析了在RTT變化範圍很大時,使用這個方法無法跟上這種變化,從而引起不必要的重傳。所以Jacobson建議除了被平滑的RTT估計器(R),所需要做的還有跟蹤RTT的方差。在往返時間變化起伏很大時,基於均值和方差來計算RTO,將比作為均值的常數倍來計算RTO能提供更好的響應。正如Jacobson所描述的,均值偏差是對標準差的一種好的逼近,但卻更容易進行計算(計算標準方差需要一個平方根)。這就引出了下面用於每個RTT測量M的公式:
Err=M-A
A←A+gErr
D←D+h(|Err|-D)
RTO=A+4D
這裡的A是被平滑的RTT(均值的估計器),而D則是被平滑的均值偏差。Err是剛得到的測量結果與當前的RTT估計器之差。A和D均被用於計算下一個重傳時間(RTO)。增量g起平均作用,取為1/8(0.125)。偏差的增益是h,取值為0.25.當RTT變化時,較大的偏差增益將使RTO快速上升。其實重傳測量結果還會存在一個問題,就是發生重傳時,然後收到一個確認,那麼這個ACK是針對第一個分組還是針對第二個分組呢?這就是所謂的重傳多義性問題。
Karn演算法
這裡又涉及到一個新的演算法,叫“Karn演算法”。他規定,當一個超時和重傳發生時,在重傳資料的確認最後到達時,不能更新RTT估計器,因為我們並不知道ACK是針對哪一次傳輸。並且,由於資料被重傳,RTO已經得到了一個指數退避,我們在下一次傳輸時使用這個退避後的RTO,對於一個沒有被重傳的報文段而言,除非收到了一個確認,否則不要計算新的RTO。
擁塞避免演算法
該演算法假定由於分組受到損壞引起丟失是非常少的(遠小於1%),因此分組丟失就意味著在源主機和目的主機之間的某處網路上發生了擁塞。有兩種分組丟失的指示:發生超時和接收到重複的確認(接收端強調我只接受到這個報文而已)。在上一學習總結中學到了一個叫“慢啟動”的限流演算法,也丟擲了一個老是慢啟動也不是辦法的問題。擁塞避免演算法和慢啟動演算法是兩個目的不同、獨立的演算法。但是當擁塞發生時,我們希望降低分組進入網路的傳輸速率,於是可以呼叫慢啟動來做到這一點。所以很多時候,在實際中這兩個演算法通常在一起實現。
擁塞避免演算法和慢啟動演算法需要對每個連線維持兩個變數:一個擁塞視窗 cwnd 和一個慢啟動門限 ssthresh 。這樣得到的演算法的工作過程如下:
1)、對於一個給定的連線,初始化cwnd為1個報文段,ssthresh為65535個位元組(注意:這是最大視窗大小);
2)、TCP輸出例程的輸出不能超過cwnd和接收方通告視窗的大小。擁塞避免是傳送方使用的流量控制,而通告視窗則是接收方進行的流量控制。前者是傳送方感受到的網路擁塞的估計,而後者則與接收方在該連線上的可用快取大小有關。
3)、當擁塞發生時(超時或收到重複確認),ssthresh被設定為當前視窗大小的一半(cwnd和接收方通告視窗大小的最小值,但最少為2個報文段)。此外,如果是超時引起了擁塞,則cwnd被設定為1個報文段(這就是慢啟動);
4)、當新的資料被對方確認時,就增加cwnd,但增加的方法依賴於我們是否正在進行慢啟動或擁塞避免。如果cwnd小於或等於ssthresh,則正在進行慢啟動,否則正在程序擁塞避免。慢啟動一直持續到我們回到當擁塞發生時所處位置一半的時候才停止(因為我們記錄了在步驟2中給我們製造麻煩的視窗大小的一半),然後專為執行擁塞避免。
在該例子當中,假定cwnd為32個報文段時就發生擁塞,於是設定shthresh為16個報文段,而cwnd為1個報文段。在時刻0傳送了一個報文段,並假定在時刻1接收到它的ACK,此時cwnd為2。接著傳送了2個報文段,並假定在時刻2接收到它的ACK,於是cwnd增加為4(對於每個ACK增加1次)。這種指數增加演算法一直進行到時刻3和4之間收到8個ACK後cwnd等於ssthresh時才停止,從該時刻開始,cwnd以線性方式增加,在每個往返時間內最多增加1個報文段。從例子可以看出,慢啟動與擁塞避免演算法的合併使用效果。那麼它們的合作是如何避免每次都要重慢啟動開始呢?
快速重傳與快速回復演算法
演算法過程如下:
1)、當收到3個重複的ACK時(1~2個ACK無法確認是丟失),將ssthresh設定為當前擁塞視窗cwnd的一半。重傳丟失的報文段,設定cwnd為ssthresh加上3倍的報文段大小(因為收到3個重複ACK)。
2)、每次收到另一個重複ACK時,cwnd增加1個報文段大小併發送1個分組(如果新的cwnd允許傳送)。
3)、當下一個確認新資料的ACK到達時,設定cwnd為ssthresh(在第1步中設定的值)。這個ACK應該是在進行重傳後的一個往返時間內對步驟1中重傳的確認。另外,這個ACK也應該是對丟失的分組和收到的第1個重複ACK之間的所有中間段報文的確認。這一步採用的是擁塞避免,因為當分組丟失時我們將當前的速率減半。
說白了就是通過3次重複ACK確認報文的丟失而不是等待超時定時器溢位的確認,這樣就避免了慢啟動演算法的啟用,而從最低初始化擁塞視窗(1個報文段)起傳送。
TCP的堅持定時器
上一篇學習總結中提到過通告視窗為0時傳送方將停止傳送資料的問題。為了避免傳送方和接收方的死迴圈等待,傳送方使用一個“堅持定時器(presist timer)”來週期性地向接收方查詢,以便發現視窗是否已經增大。這些從傳送方發出的報文段稱為視窗查探(window probe)。
從上圖可以看出,堅持定時器使用了普通的TCP指數退避。對於一個典型的區域網連線,首次超時時間算出來是1.5秒,第2次的超時時值增加一倍,為3秒,再下次乘以4為6秒,之後再乘以8為12秒等,但是堅持定時器總是在5~60秒之間。視窗探查包含一個位元組的資料(以上例子序號為4098)。TCP總是允許傳送已關閉視窗之後一個位元組的資料。堅持狀態與重傳超時之間一個不同的特點就是TCP從不放棄傳送視窗探查(重傳超時會持續9分鐘),這些探查每隔60秒傳送一次,這個過程將持續到或者視窗被開啟或應用程序使用的連線被終止。
在上一篇總結當中同樣提到了一個叫“糊塗視窗綜合徵”的問題。也就是說,接收方每出現哪怕1個位元組的空閒視窗也會通告發送方,那麼傳送放就會立刻傳送資料,但就會讓傳送方與接收方陷入“小分組”傳送的死迴圈,從而會造成網路擁塞問題。該現象可以發生在兩端的任何一端,接收方可以通告一個小的視窗(而不是一直等到有大的視窗時才通知),而傳送方也可以傳送少量資料(而不是等待其他的資料以便傳送一個大的報文段)。可以在任何一端採取措施避免出現“糊塗視窗綜合徵”的現象:
1)、接收方不通告視窗大小。通常的演算法是接收方不通告一個比當前視窗大的視窗(可以為0)除非視窗可以增加一個報文段大小(也就是將要接受的MSS)或者可以增加接收方快取空間的一半,不論實際有多少;
2)、傳送方避免出現“糊塗視窗綜合徵”的措施只有以下條件之一滿足時才傳送資料:(a)可以傳送一個滿長度的報文段;(b)可以傳送至少是接收方通告視窗大小一半的報文段;(c)能夠傳送手頭的所有資料並且不希望接收ACK(也就是說,我們沒有還未確認的資料)或該連線禁止了Nagle演算法。
TCP的保活定時器
保活定時器主要是為伺服器應用程式提供的,因為許多時候一個伺服器希望知道客戶主機是否崩潰並關機或者崩潰又重新啟動。但有一點需要注意:
保活定時器並不是TCP規範中的一部分。Host Requirements RFC提供了3個不使用保活定時器的理由:(1)在出現短暫差錯的情況下,這可能會使一個非常好的連線釋放掉;(2)它們耗費不必要的頻寬;(3)在按分組計費的情況下會在網際網路上花掉更多的錢。然而,許多實現提供了保活定時器。
如果一個給定的連線在兩個小時之內沒有任何動作,則伺服器就向客戶傳送一個探查報文段,客戶主機必須處於以下4個狀態之一:
1)、客戶主機依然正常執行,並從伺服器可達。客戶的TCP響應正常,而伺服器也知道對方是正常工作的。伺服器在兩個小時以後將保活定時器復位。如果在兩個小時定時器到時間之前有應用程式的通訊量通過此連線,則定時器在交換資料後的未來2小時再復位。
2)、客戶主機已經崩潰,並且關閉或者正在重新啟動。在任何一種情況下,客戶的TCP都沒有響應。伺服器將不能夠收到對探查的響應。並在75秒後超時。伺服器總共傳送10個這樣的探查,每個間隔75秒。如果伺服器沒有收到一個響應,它就認為客戶主機已經關閉或終止連線。
3)、客戶主機已經崩潰並已經重新啟動。這是伺服器將收到一個對其保活探查的響應,但是這個響應是一個復位,使得伺服器終止這個連線。
4)、客戶主機正常執行,但是從伺服器不可達。這與狀態2相同,因為TCP不能夠區分狀態4與狀態2之間的區別,它所能發現的就是沒有收到探查的響應。
學習總結
本篇章主要針對TCP管理的4個定時器的介紹,包括超時重傳定時器、堅持定時器、保活定時器,以及第一篇章介紹的2MSL的TIME_WAIT狀態定時器。這些都是確保TCP可靠性的重要手段。