TCP重傳機制


 

 

TCP重傳機制

在錯綜復雜的網絡,並不一定所有的數據能正常的數據傳輸,萬一數據在傳輸過程中丟失了呢?

TCP要保證所有的數據包都可以到達,所以,必需要有重傳機制。

常見的重傳機制:

  • 超時重傳
  • 快速重傳
  • SACK
  • D-SACK

而所有重傳的機制都需要依賴通過序列號Seq與確認應答ACK。

在 TCP 中,當發送端的數據到達接收主機時,接收端主機會返回一個確認應答消息,表示已收到消息。

 

 

 

超時重傳

  發送端發了1,2,3,4,5一共五份數據,接收端收到了1,2,於是回ack 3,然后收到了4(注意此時3還沒收到),此時的TCP會怎么辦?我們要知道,因為正如前面所說的,SeqNum和Ack是以字節數為單位,所以ack的時候,不能跳着確認,只能確認最大的連續收到的包,不然,發送端就以為之前的都收到了。

超時重傳的處理方式:接收端不再回ack(直到收到數據3),發送端死等ack 3,當發送端發現收不到3的ack超時后,會重傳3。一旦接收端收到3后,會ack 回 4——意味着3收到了,期待下一個數據4。

但是,這種方式會有比較嚴重的問題,那就是因為要死等3,所以會導致4和5即便已經收到了,而發送端也完全不知道發生了什么事,因為沒有收到Ack,所以,發送方可能會悲觀地認為也丟了,所以有可能也會導致4和5的重傳。

對此有兩種選擇:

  • 一種是僅重傳timeout的包。也就是第3份數據。
  • 另一種是重傳timeout后所有的數據,也就是第3,4,5這三份數據。

 

TCP 會在以下兩種情況發生超時重傳:

  • 數據包丟失
  • 確認應答丟失

超時時間

我們先來了解一下什么是 RTT(Round-Trip Time 往返時延),從下圖我們就可以知道:

RTT 就是數據從網絡一端傳送到另一端所需的時間,也就是包的往返時間。

超時重傳時間是以 RTO (Retransmission Timeout 超時重傳時間)表示。

假設在重傳的情況下,超時時間 RTO 「較長或較短」時,會發生什么事情呢?

上圖中有兩種超時時間不同的情況:

  • 當超時時間 RTO 較大時,重發就慢,丟了老半天才重發,沒有效率,性能差;
  • 當超時時間 RTO 較小時,會導致可能並沒有丟就重發,於是重發的就快,會增加網絡擁塞,導致更多的超時,更多的超時導致更多的重發。

精確的測量超時時間 RTO 的值是非常重要的,這可讓我們的重傳機制更高效。

根據上述的兩種情況,我們可以得知,超時重傳時間 RTO 的值應該略大於報文往返 RTT 的值

至此,可能大家覺得超時重傳時間 RTO 的值計算,也不是很復雜嘛。

好像就是在發送端發包時記下 t0 ,然后接收端再把這個 ack 回來時再記一個 t1,於是 RTT = t1 – t0。沒那么簡單,這只是一個采樣,不能代表普遍情況

實際上「報文往返 RTT 的值」是經常變化的,因為我們的網絡也是時常變化的。也就因為「報文往返 RTT 的值」 是經常波動變化的,所以「超時重傳時間 RTO 的值」應該是一個動態變化的值

我們來看看 Linux 是如何計算 RTO 的呢?

估計往返時間,通常需要采樣以下兩個:

  • 需要 TCP 通過采樣 RTT 的時間,然后進行加權平均,算出一個平滑 RTT 的值,而且這個值還是要不斷變化的,因為網絡狀況不斷地變化。
  • 除了采樣 RTT,還要采樣 RTT 的波動范圍,這樣就避免如果 RTT 有一個大的波動的話,很難被發現的情況。

RFC6289 建議使用以下的公式計算 RTO:

其中 SRTT 是計算平滑的RTT ,DevRTR 是計算平滑的RTT 與 最新 RTT 的差距。

在 Linux 下,α = 0.125,β = 0.25, μ = 1,∂ = 4。別問怎么來的,問就是大量實驗中調出來的。

如果超時重發的數據,再次超時的時候,又需要重傳的時候,TCP 的策略是超時間隔加倍。

也就是每當遇到一次超時重傳的時候,都會將下一次超時時間間隔設為先前值的兩倍。兩次超時,就說明網絡環境差,不宜頻繁反復發送。

超時觸發重傳存在的問題是,超時周期可能相對較長。那是不是可以有更快的方式呢?

於是就可以用「快速重傳」機制來解決超時重發的時間等待。

 

快速重傳

不以時間驅動,而以數據驅動重傳

接收端如果沒有收到期望的數據,而收到后續亂序的包,也給客戶端回復 ACK,只不過是重復的 ACk,回復相同的ACK三次以后觸發快速重傳。

也就是說,如果,包沒有連續到達,就ack最后那個可能被丟了的包,如果發送方連續收到3次相同的ack,就重傳。Fast Retransmit的好處是不用等timeout了再重傳。

比如:如果發送方發出了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。示意圖如下:

 

 

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

 

SACK 

SACK(Selective Acknowledgment),在快速重傳的基礎上,返回最近收到的報文段的序列號范圍,這樣客戶端就知道,哪些數據包已經到達服務器了。

如下圖,發送方收到了三次同樣的 ACK 確認報文,於是就會觸發快速重發機制,通過 SACK 信息發現只有 200~299 這段數據丟失,則重發時,就只選擇了這個 TCP 段進行重復。

如果要支持 SACK,必須雙方都要支持。在 Linux 下,可以通過 net.ipv4.tcp_sack 參數打開這個功能(Linux 2.4 后默認打開)。

 

Duplicate SACK 

DSACK,即重復 SACK,這個機制是在 SACK 的基礎上,額外攜帶信息,告知發送方有哪些數據包自己重復接收了。DSACK 的目的是幫助發送方判斷,是否發生了包失序、ACK 丟失、包重復或偽重傳。讓 TCP 可以更好的做網絡流控。

栗子一號:ACK 丟包

  • 「接收方」發給「發送方」的兩個 ACK 確認應答都丟失了,所以發送方超時后,重傳第一個數據包(3000 ~ 3499)
  • 於是「接收方」發現數據是重復收到的,於是回了一個 SACK = 3000~3500,告訴「發送方」 3000~3500 的數據早已被接收了,因為 ACK 都到了 4000 了,已經意味着 4000 之前的所有數據都已收到,所以這個 SACK 就代表着 D-SACK
  • 這樣「發送方」就知道了,數據沒有丟,是「接收方」的 ACK 確認報文丟了。

栗子二號:網絡延時

  • 數據包(1000~1499) 被網絡延遲了,導致「發送方」沒有收到 Ack 1500 的確認報文。
  • 而后面報文到達的三個相同的 ACK 確認報文,就觸發了快速重傳機制,但是在重傳后,被延遲的數據包(1000~1499)又到了「接收方」;
  • 所以「接收方」回了一個 SACK=1000~1500,因為 ACK 已經到了 3000,所以這個 SACK 是 D-SACK,表示收到了重復的包。
  • 這樣發送方就知道快速重傳觸發的原因不是發出去的包丟了,也不是因為回應的 ACK 包丟了,而是因為網絡延遲了。

可見,D-SACK 有這么幾個好處:

  1. 可以讓「發送方」知道,是發出去的包丟了,還是接收方回應的 ACK 包丟了;
  2. 可以知道是不是「發送方」的數據包被網絡延遲了;
  3. 可以知道網絡中是不是把「發送方」的數據包給復制了;

在 Linux 下可以通過 net.ipv4.tcp_dsack 參數開啟/關閉這個功能(Linux 2.4 后默認打開)。

 

筆記整理:

TCP 的那些事兒(上)

【圖解】你還在為 TCP 重傳、滑動窗口、流量控制、擁塞控制發愁嗎?看完圖解就不愁了

TCP的快速重傳機制


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM