網絡編程基礎(5)-協議概要-TCP的超時重傳

在兩端通信過程中,發送的數據和確認應答都存在丟失的可能。如下圖:
這裏寫圖片描述這裏寫圖片描述

對此,TCP規定發送數據時設置一個重傳定時器,如果在定時器溢出的時候還沒有收到該數據的確認,那麼重傳該數據。
一方面TCP確實需要重傳丟失的數據以保證傳輸的可靠性,另一方面又要盡力減少不必要的重傳,以防止網絡性能下降。對任何實現而言,這關鍵之處就在於如何確定超時時間和重傳的頻率。

重傳超時時間RTO的測量

這裏涉及兩個重要的時間概念。
往返時間RTT:指發送端從發送TCP報文段開始到收到該報文段的確認所經過的時間。
超時時間RTO:指發送端從TCP段發送報文段到重傳數據前等待接收確認應答的這段時間。

圖示如下:

這裏寫圖片描述

RTT的測量

根據定義和上圖所示,RTT的計算不難。但是這裏要注意一個問題,多數源於伯克利的TCP實現在任何時候對每個連接僅測量一次,意即如果發送一個報文段時,定時器已經被使用,則該報文段不被計時。上圖中T1時刻發送了報文段data-1,此時開啓定時器,隨後在發送端收到確認報文段ack-1之前還發送了報文段data-2,但是並不再啓用定時器,因爲此時已經有一個定時器在計時了。

一種RTO計算方法

那麼RTO如何確定?由於網絡情況是隨時變化的,RTO肯定不能是一個定值。如果RTO設長了,重發就慢,沒有效率,性能差。而如果RTO設短了,重發的就快,導致更多的超時,更多的超時導致更多的重發,增大網絡負載,最後可能導致網絡擁塞。

這裏介紹一種RTO計算方法:TCP在每次發包時都會計算平滑往返時間SRTT及其平滑均值偏差D,將這個SRTT和D相加,RTO就是比這個總和要稍微大一點的值。
即:RTO=SRTT+D+e ,其中e就是那個稍微大一點的值。

計算RTO時同時考慮往返時間和偏差是有其原因的,實際網絡環境複雜,數據包的分段可能通過不同線路到達,單次測量的不同往返時間可能會產生大幅度的搖擺,如下圖所示。同時考慮往返時間和均值偏差,再加上一個增量,可以保證絕大多數時間內RTO都是一個很合適的值。

這裏寫圖片描述

另外,很多系統TCP實現中規定最小重傳時間,典型值200ms。
更加詳細先進的計算方法可參考相關資料。

Karn算法

重傳二義性

在報文段重傳時會產生這樣的問題:發送端發出了一個報文段,並且設定的超時時間到了,但沒有收到確認,於是重傳該報文段。經過了一段時間後,收到了確認。現在的問題是:如何判定此確認報文段是對先發送的報文段的確認,還是對後來重傳的報文段的確認?由於重傳的報文段與原先的報文段完全一樣,因此發送方在收到確認後就無法做出正確的判斷。這種現象稱爲確認二義性(TCP首部裏的時間戳選項,是可以避免確認二義性的),將導致TCP無法準確地估算RTT。
如下圖示:
這裏寫圖片描述
情況(a)是ack沒回來,所發重傳。如果以第一次發送的時間和ACK的時間計算RTT,那明顯算大了。
情況(b)是ack回來慢了,重傳沒多久,之前ACK的就回來了。如果你以重傳的時間和ACK回來的時間計算RTT,那就會算小了。

Karn算法及其修正

爲此,Karn算法規定:當一個超時和重傳發生時,不計算重傳的RTT。

然而這可能會引起新的問題。如果報文段的RTT突然增大了很多使得(RTT > RTO),那麼在原來的RTO內不會收到確認報文段,發送方就會誤認爲丟包了,於是發送方重傳報文段,但是並沒有更新RTT,從而RTO的值也沒有得到更新,其後果就是在當前大RTT下,幾乎造成每個報文段都被重傳,造成了資源的大大浪費。
爲此考慮對Karn算法進行修正。方法是:報文段每重傳一次,就將RTO增大一些。新的RTO = γ×(舊的RTO),其中係數γ的典型值是2。當不再發生報文段的重傳時,纔會計算SRTT和平滑均值偏差D,以便重新估計RTO。

時間戳選項

更好的RTT測量機制

上面介紹的RTT計算方法對每個窗口的RTT僅進行一次測量。並不能對每個報文段都測量。前文介紹過帶寬時延乘積,若一個TCP連接的帶寬時延乘積很大,那這樣的TCP連接被稱爲長肥管道。而在長肥管道上需要更好的RTT測量機制,它能對每個報文段計時,包括重傳。

發送方在每個報文段中放置一個時間戳值。接收方在確認中返回這個數值。從而允許發送方爲每一個收到的ACK計算RTT。主動發起連接一方在它的SYN中指定該選項,只有在它從另一方的SYN中收到了這個選項之後,該選項纔會在以後的報文中進行設置。

這裏注意下:該時間戳值並不是一個現實的時間值,它是在啓動時給一個固定初值(一般爲0)以後每隔一定時間(1毫秒到1秒之間,200ms是一個典型值)將時間戳時鐘加1。

防止序號迴繞

TCP首部中序列號爲32位,在長肥管道中,這個32位值的序號很可能在一個MSL(報文段最大生存時間)內用盡而重新開始,這時可能出現相同序號的報文段。那如何區分呢?有了時間戳選項,可以區分這種情況。接收方將時間戳視爲序列號的一個32位擴展。當出現兩個序號相同而時間戳不同的報文時,TCP將丟棄時間戳較小的報文段,稱之爲PAWS算法。

快速重傳

在接收方收到一個失序報文段時,TCP會立即產生一個ACK,這個ACK與上一個ACK相同且不應該被延遲。該重複的ACK目的在於在對方知道收到一個失序的報文段,並告訴對方自己希望收到的報文段。

由於我們不知道一個重複的ACK是由一個丟失的報文段引起的,還是由於僅僅出現了幾個報文段的重新排序,因此我們等待少量重複的ACK到來。假如這只是一些報文段的重新排序,則在重新排序的報文段被處理併產生一個新的ACK之前,只可能產生1~2個重複的ACK。如果一連串收到 3個或3個以上的重複ACK,就非常可能是一個報文段丟失了。於是我們就重傳丟失的數據報文段,而無需等待超時定時器溢出。

比如,如果發送方發出了1,2,3,4,5份數據,第一份先送到了,於是就ack回2,結果2因爲某些原因沒收到而3到達了,於是還是ack回2,後面的4和5都到了,但是還是ack回2,因爲2還是沒有收到,於是發送方收到了三個ack=2的確認,知道了2還沒有到,於是就馬上重傳2。然後,接收端收到了2,此時因爲3,4,5都收到了,於是ack回6。示意圖如下:
這裏寫圖片描述

這種機制比之前的超時重發更高效,所以稱爲快速重傳。

SACK選項

當然快速重傳只是解決了超時重傳的問題,它依然面臨一個艱難的選擇。對於上面的示例來說,是重傳#2呢還是重傳#2,#3,#4,#5呢?因爲發送端並不清楚這連續的3個ack(2)是誰傳回來的?也許發送端發了20份數據,是#6,#10,#20傳來的呢。這樣,發送端很有可能要重傳從2到20的這堆數據(這就是某些TCP的實際的實現)。可見,這是一把雙刃劍。

TCP首部選項裏面有個SACK選項,這個SACK選項可以使發送端只重傳丟失的數據。如下圖:
這裏寫圖片描述

接收方維護接收緩存,在回覆的ACK報文段首部SACK選項裏面捎帶需要重傳的報文段字節序號,發送端據此重新發送這些丟失的報文段。

TCP的保活功能

一個TCP連接並不會一直存在數據交互,有些連接會在數據交互完畢後,主動釋放連接,而有些不會。那麼在長時間無數據交互的時間段內,交互雙方都有可能出現掉電、死機、異常重啓等各種意外。當這些意外發生之後,這些TCP連接並未來得及正常釋放。那麼,連接的另一方並不知道對端的情況,它會一直維護這個連接,長時間的積累會導致非常多的半打開連接,造成系統資源的消耗和浪費。爲了解決這個問題,在傳輸層可以利用TCP的保活報文來實現。

保活的操作過程

如果一個給定的連接在一定時間內(默認兩小時,可更改)沒有任何動作,則服務器就向客戶發送一個探查報文段。如果連接正常,客戶正常響應。如果連接不正常,將會每隔一定時間重新發送保活探測報文,直到發送一定數量還是沒有響應,則認定客戶已經斷開連接,並釋放該連接。
保活的工作過程可如下圖表示:
這裏寫圖片描述

保活作用

保活功能主要是爲服務器應用程序提供,服務器應用程序可能會代表客戶綁定一些資源,因此希望知道客戶主機是否崩潰。

1:探測連接的對端是否存活
在應用交互的過程中,可能存在以下情況:
1)客戶端或服務器端意外斷電、死機、崩潰、重啓。
2)中間網絡已經中斷,而客戶端與服務器端並不知道。
利用保活探測功能,可以探知這種對端的意外情況,從而保證在意外發生時,可以釋放半打開的TCP連接。

2:防止中間設備因超時刪除連接相關的連接表
中間設備如防火牆等,會爲經過它的數據報文建立相關的連接信息表,併爲其設置一個超時時間的定時器,如果超出預定時間,某連接無任何報文交互的話,中間設備會將該連接信息從表中刪除,在刪除後,再有應用報文過來時,中間設備將丟棄該報文,從而導致應用出現異常。爲了解決這個問題,應用本身或TCP可以通過保活報文來維持中間設備中該連接的信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章