在前面咱們概述了TCP的超時重傳以後咱們簡單的看一下tcp超時重傳的示例。首先簡單的描述一下測試過程linux
一、設置/proc/sys/net/ipv4/tcp_early_retrans爲2,關掉TLP功能(後面內容介紹TLP)。設置/proc/sys/net/ipv4/tcp_retries2爲8,減小重傳次數,這樣方便wireshark抓包演示。同時設置/proc/sys/net/ipv4/tcp_discard_on_port爲9877,以讓client能夠精確的控制發出的TCP報文,而不受到內核TCP模塊的影響。服務器
二、client經過raw socket直接在IP層之上構造TCP報文,與server經過三次握手創建鏈接,其中client爲127.0.0.1:10000,server爲127.0.0.1:9877。這個步驟對應No1--No3報文。網絡
三、TCP鏈接創建後client發送"hello"消息給服務器,服務器則回覆ACK,這個步驟對應No4--No5報文。socket
四、server發送「hello」給client,這個步驟對應No6報文。tcp
五、client收到server的"hello"報文後,直接丟棄並不回覆ACK。測試
六、server在沒有收到ACK報文的狀況下,過了大約1.5s後,server端RTO超時,觸發超時重傳。client一樣在收到這些重傳報文的時候直接丟棄而不回覆ACK,這樣server持續重傳。一共重傳了6次。spa
這個過程的wireshark截圖以下(爲了方便觀察RTO,我把No6初傳設置爲時間參考點,後面數據包的Time時間都是相對於No6包的時間):code
從這個超時重傳示例中咱們重點關注幾個方面orm
一、能夠看到No7第一次重傳與No6初傳間隔大約爲1.5s,No8第二次重傳與No7時間間隔大約是3.0s,No9第三次重傳與No8時間間隔大約是6.0s。能夠看到每次重傳後,重傳的時間間隔(即RTO)都是上次重傳時間間隔的2倍。實際咱們從server端程序能夠獲取到Linux內核中TCP模塊計算的RTO分別爲1.504s、3.008s、6.016s。也能夠精確的看到RTO的倍增關係。這種RTO倍增關係就是指數回退(binary exponential backoff)。server
二、咱們在測試過程描述中說明設置tcp_retries2爲8,而從wireshark中能夠看到實際只是發生了6次重傳。實際上linux內核會根據tcp_retries2計算出一個timeout,計算方式以下面代碼所示,其中對於established狀態下的endpoint,rto_base爲200ms,TCP_RTO_MAX爲120000ms。當tcp_retries2設置爲8的時候,按照下面流程計算得出timeout=102.2s。其中No11和No12之間的RTO爲48s左右(根據server端實際獲取的信息,linux內部實際計算出的RTO爲48.128s),若是進一步指數回退,則下一次重傳時間點大約是48*2+94=190s,能夠看到190s已經超過了計算出的timeout(即102.2s)值,應該不會進一步進行重傳,屢次超時重傳失敗server端則會直接釋放這個tcp鏈接,釋放這個鏈接後若是server端嘗試進一步寫入數據就會返回ETIMEDOUT的錯誤。
if (tcp_retries2 <= 9) timeout = ((2 << tcp_retries2) - 1) * rto_base; else timeout = ((2 << 9) - 1) * rto_base + (tcp_retries2 - 9) * TCP_RTO_MAX;
在RFC1122中有兩個門限R1和R2,當重傳次數超過R1的時候,TCP向IP層發送negative advice,指示IP層進行MTU探測、刷新路由等過程,以防止因爲網絡鏈路發生變化而致使TCP傳輸失敗。當重傳次數超過R2的時候,TCP放棄重傳並關閉TCP鏈接。其中R1和R2也能夠表述爲時間,即總重傳時間超過R1或者R2的時候觸發響應的操做。在linux中對於普通數據報文狀態下的TCP,R1對應/proc/sys/net/ipv4/tcp_retries1,R2對應/proc/sys/net/ipv4/tcp_retries2參數。這兩個參數都是根據上面的計算流程計算出一個timeout值,當總重傳時間超過這個timeout值尚未收到ack的時候觸發響應的操做。對於SYN報文如咱們以前所講,則是由tcp_syn_retries和tcp_synack_retries這兩個參數控制。
補充說明:
一、linux中根據tcp_retries1和tcp_retries2計算timeout的過程參考代碼retransmits_timed_out。
二、網上不少資料以及man 7 tcp和第二版的tcpip詳解中對於tcp_retries1或tcp_retries2描述都是按照重傳次數來描述的,其實是錯誤的。