TCP系列16—重傳—六、基礎快速重傳(Fast Retransmit)

1、快速重傳介紹linux

        按照TCP協議,RTO超時重傳是一個很是重要的事件,當RTO超時的時候,TCP會同時經過兩種方式很是謹慎的下降發送數據包的速率,一種是基於擁塞控制削減發送窗口的大小,另一個是經過指數回退增長每次RTO超時的時間(即karn算法的第二部分)。因此RTO超時後有可能會致使網絡容量的利用不足。算法

        最開始咱們介紹tcp重傳的時候就介紹過TCP還有另一種重傳方式--快速重傳。快速重傳是RFC5681定一個的一個過程。快速重傳不依賴定時器的超時,而是依靠ACK確認包來進行重傳。使用快速重傳相比RTO超時重傳一般能夠更高效的修復TCP丟包問題。快速重傳是基於一個前提:即按照RFC5681,當TCP收到一個亂序報文的時候應該當即回覆ACK確認包,而不會延遲ACK(延遲ACK介紹參考以前文章介紹)確認。另外RFC5681還指出若是接收序列號空間存在洞,新接收的報文徹底填充了這個洞或者部分填充了這個洞,TCP也應該當即回覆一個ACK確認包以便發送端及時獲取接收端相關的信息。緩存

        咱們舉個例子假設有5個TCP報文,P1(1-10)、P2(11-20)、P3(21-30)、P4(31-40)、P5(41-50),其中括號中標註的是報文的比特系列號,每一個報文的長度都爲10bytes。假設發送端依次發送這5個報文,其中P2報文在網絡傳輸過程當中丟失,P一、P三、P四、P5報文依次按序到達,接收端收到這P1的時候發送ack=11的確認包(實際上這裏可能會延遲發送ACK報文,爲了描述簡單咱們假設當即發送ACK報文),接收端收到P3的時候發現是亂序的報文則會當即回覆ack=11確認包(還記得ACK是累計確認的吧,由於P2丟失了ACK只能累計到11),一樣後面收到P4和P5的時候仍是會回覆ack=11的確認包。這樣發送端就會連續接收到4個ack=11的確認包,後面三個確認包由於和第一個ack number重複,由於稱呼爲duplicate ACK。所以接收端就能夠依據dup ACK來推測接收端的接收狀況。可是咱們以前說過IP層不會向TCP提供有序的數據報文,若是網絡傳輸過程當中發生亂序致使接收端接收順序變爲P一、P三、P二、P四、P5,這樣的狀況下也會產生一個dup ACK。另外還有一種狀況是IP層dup了ACK報文。咱們經過一個dup ACK並不能可靠的確認是發生了丟包仍是發生了亂序傳輸,所以會存在一個門限(duplicate ACK threshold或者叫作dupthresh),當TCP收到的dup ACK數超過這個門限的時候,就會認爲發生了丟包,進而初始化一個快速重傳。最初協議中給出的dupthresh這個門限是3,可是RFC 4653給出了一種調整dupthresh的方法。Linux中則能夠經過/proc/sys/net/ipv4/tcp_reordering來設置默認值,另外Linux可能還會根據亂序測量的結果來更新實際的dupthresh。dupthresh的範圍最終會在/proc/sys/net/ipv4/tcp_reordering和/proc/sys/net/ipv4/tcp_max_reordering之間。在沒有使能SACK的時候,快速重傳只會重傳一個數據包,在使能SACK時候,SACK能夠反映接收端是否存在系列號洞,進而容許發送端根據SACK的狀況同時傳輸多個數據包。SACK的內容留到後面介紹。服務器

 

      最後補充一下window update的判斷,通常若是一個TCP報文知足下面三個條件之一的話,linux就會認定這個報文是window update消息,被認定爲window update的確認包是不會統計到dup ACK裏面的,後面介紹窗口管理的時候還會進一步介紹一下window update。網絡

一、ack number比以前接收的最大的ack number還要大socket

二、系列號seq比以前接收到的最大系列號還要大tcp

三、系列號seq與以前接收到的系列號相同,可是TCP頭中的window size字段發生了變化測試

 

2、wireshark示例spa

  一、快速重傳與RTO超時orm

設置/proc/sys/net/ipv4目錄下tcp_retries2=8,tcp_early_retrans=0,tcp_sack=0,tcp_reordering=3,tcp_discard_on_port =9877(該參數爲自加參數)。

  • client經過rawsocket與server創建鏈接,對應No1-No3報文

  • client先發送6bytes的數據,服務器回覆ACK確認包,對應No4-No5報文

  • 服務器連續發送5個len=8的報文,對應No6-No10

  • client對No6報文回覆ACK確認報文,對應No11

  • client丟棄No7報文來模擬傳輸過程當中丟包,並對No8-No10報文回覆dupACK,對應No12-No14

  • server端在收到三個dup ACK後,認爲No7報文已經丟失並當即觸發快速重傳,同時設置RTO超時定時器,對應No15

  • client對以後收到的報文直接丟棄不在回覆ACK確認包

  • server端RTO超時後,繼續重傳對應的報文,並進行指數回退過程。最終屢次RTO超時重傳失敗後,server端釋放TCP鏈接,對應No16-No21。

二、快速重傳與window update消息

設置/proc/sys/net/ipv4目錄下tcp_retries2=8,tcp_early_retrans=0,tcp_sack=0,tcp_reordering=3,tcp_discard_on_port =9877(該參數爲自加參數)。

  • client經過rawsocket與server創建鏈接,對應No1-No3報文

  • client先發送6bytes的數據,服務器回覆ACK確認包,對應No4-No5報文

  • 服務器連續發送5個len=8的報文,對應No6-No10

  • client丟棄No6報文來模擬傳輸過程當中丟包,並對No7-No10報文回覆dupACK,對應No11-No14

  • 從wireshark抓包看,server端在收到四個dup ACK後,才認爲No6報文已經丟失並當即觸發快速重傳,同時設置RTO超時定時器,對應No15

  • client對以後收到的報文直接丟棄不在回覆ACK確認包

  • server端RTO超時後,繼續重傳對應的報文,並進行指數回退過程。最終屢次RTO超時重傳失敗後,server端釋放TCP鏈接,對應No16-No21。

那麼這裏問題來了,咱們設置的tcp_reordering爲3,也就是dupthresh值爲3(實際上dupthresh能夠動態調整,可是在此次測試中沒有發生調整),那爲何這裏快速重傳的觸發須要四個dup ACK呢?緣由是這裏No11對應的ACK報文,相比於No4的ACK報文雖然ack number都爲3741168164,可是二者的Seq卻發生了變化,所以linux認爲No11是一個window update消息,而linux並不把window update消息計入dup ACK,後面收到的No12-No14報文才會被TCP認爲是dup ACK,所以直到收到No14報文TCP纔會認爲dup ACK達到快速重傳門限,觸發快速重傳。這裏也反映了wireshark和linux在dup ACK認定上的差別性

三、快速重傳與recovery point

       下面咱們在看一個快速重傳後觸發RTO超時重傳而後收到ACK確認包的場景。這個測試過程與上一個示例相似,差別部分在於接收端在收到No18的重傳報文以及以後的報文的時會回覆一個ACK確認包。咱們能夠看到在觸發快速重傳以前,正常傳輸的數據中最高系列號的下一個待發送系列號爲No10報文對應的1751769800(即1751769792+8),這個系列號節點稱呼爲recovery point,而No19確認包中Ack=1751769768小於以前的1751769800,對於這種ACK確認報文,TCP稱呼爲partial ACK。在TCP收到parital ACK報文的時候則會當即觸發快速重傳,如No20、No2二、No2三、No25所示。截圖中最後一個RST消息則是因爲server端應用層沒有讀取server緩存中的數據(No4報文的數據)而直接close,所以會產生RST消息。實際的消息流中還有一個No29消息則是因爲client使用raw socket編寫的程序沒有處理server端的RST消息,client關閉的時候簡單的發送了一條RST消息來通知server。

       咱們同時注意到,在發送端收到No19一個ACK確認包的時候,只是發出去了一個重傳包,而在收到No21確認包的時候,TCP卻發出去了兩個重傳包。這種差別則是因爲TCP的擁塞控制形成的,後面咱們講到擁塞控制的時候在經過幾個示例來介紹說明。

 

 

補充說明:

一、快速重傳代碼點tcp_fastretrans_alert,window update消息和dup ack的判斷在tcp_ack_update_window和tcp_ack中

 

 

 

 

 

 

 

 

 

 



相關文章
相關標籤/搜索