TCP-重傳機制(分享)

一、概述

在TCP中,當發(fā)送端的數(shù)據(jù)到達接收主機時,接收端主機會返回一個確認應答消息,表示已收到消息,這是TCP實現(xiàn)可靠傳輸?shù)牟呗灾弧?/p>

但在錯綜復雜的網(wǎng)絡,并不一定能如上圖那么順利能正常的數(shù)據(jù)傳輸,萬一數(shù)據(jù)在傳輸過程中丟失了呢?

?所以 TCP 針對數(shù)據(jù)包丟失的情況,會用重傳機制解決。

常見的重傳機制:

超時重傳

快速重傳

SACK

D-SACK

二、超時重傳

在發(fā)送數(shù)據(jù)時,設定一個定時器,當超過指定的時間后,沒有收到對方的ACK確認應答報文,就會重發(fā)該數(shù)據(jù),就是我們常說的超時重傳。

TCP會在以下兩種情況發(fā)生超時重傳

數(shù)據(jù)包丟失

確認應答丟失

既然是超時重傳,那么超時時間應該設置為多少呢?

RTT(Round-Trip Time 往返時延)

RTO(Retransmission Timeout 超時重傳時間)

假設在重傳的情況下,超時時間RTO「較長或較短」時,會發(fā)生什么事情呢?下圖中有兩種超時時間不同的情況。

當超時時間RTO 較大時,重發(fā)就慢,效率低,性能差;

當超時時間RTO 較小時,會導致可能并沒有丟就重發(fā),于是重發(fā)的就快,會增加網(wǎng)絡擁塞,導致更多的超時,更多的超時導致更多的重發(fā)。

根據(jù)以上情況,我們可以得知,超時重傳時間 RTO 的值應該略大于報文往返RTT 的值。

是否可以這樣處理?

發(fā)送端發(fā)包時記下t0,收到接收端ack回來后記t1, RTT = t1- t0; RTO 設置成一個略大于RTT的值。

因為我們的網(wǎng)絡也是時常變化的。也就因為「報文往返RTT 的值」是經(jīng)常波動變化的,所以「超時重傳時間 RTO 的值」應該是一個動態(tài)變化的。

Linux 是如何計算RTO?

核心問題是要通過采樣RTT時間,然后進行加權平均,算出一個平滑RTT的值,而且這個值還因為網(wǎng)絡是不斷變化的原因是一個不斷變化的值。

Jacobson / Karels 算法

SRTT?= SRTT?+ α?(RTT?– SRTT) ?—— 計算平滑RTT

DevRTT?= (1-β)*DevRTT?+ β*(|RTT-SRTT|) ——計算平滑RTT和真實的差距(加權移動平均)

RTO= μ *?SRTT + ? *DevRTT

在 Linux 下,α = 0.125,β = 0.25, μ = 1,? = 4。別問怎么來的,問就是大量實驗中調出來的。這個算法在被用在Linux 源代碼的TCP協(xié)議中。

如果超時重發(fā)的數(shù)據(jù),再次超時的時候,又需要重傳的時候,TCP 的策略是超時間隔加倍。

iOS 系統(tǒng)中,TCP RTO 的超時實驗數(shù)據(jù):[ 1s,1s,1s,2s,4.5s,9s,13.5s,26s,26s … ]

二、快速重傳

超時重傳存在的問題是,超時周期相對較長。TCP 還有一種快速重傳機制,不以時間為驅動,而是以數(shù)據(jù)為驅動。

圖中,發(fā)送方發(fā)出了 1,2,3,4,5 份數(shù)據(jù):

第一份 Seq1 先送到了,于是就 Ack 回 2;

結果 Seq2 因為某些原因沒收到,Seq3 到達了,于是還是 Ack 回 2;

后面的 Seq4 和 Seq5 都到了,但還是 Ack 回 2,因為 Seq2 還是沒有收到;

發(fā)送端收到了三個 Ack = 2 的確認,知道了 Seq2 還沒有收到,就會在定時器過期之前,重傳丟失的 Seq2。

最后,收到了 Seq2,此時因為 Seq3,Seq4,Seq5 都收到了,于是 Ack 回 6 。

所以,快速重傳的工作方式是當收到三個相同的 ACK 報文時,會在定時器過期之前,重傳丟失的報文段。

快速重傳機制只解決了一個問題,就是超時時間的問題,但是它依然面臨著另外一個問題。就是重傳的時候,是重傳之前的一個,還是重傳所有的問題。

三、SACK方法

還有一種重傳的方式叫:SACK (Selective Acknowledgment 選擇性確認)

這種方式需要在 TCP 頭部「選項」字段里加一個SACK的東西,它可以將緩存的地圖發(fā)送給發(fā)送方。

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

如果要支持SACK,必須雙方都要支持。在 Linux 下,可以通過net.ipv4.tcp_sack參數(shù)打開。

四、Duplicate SACK

Duplicate SACK 又稱D-SACK,其主要使用了 SACK 來告訴「發(fā)送方」有哪些數(shù)據(jù)被重復接收了。下面舉例兩個栗子,來說明D-SACK的作用。

例子1:ACK 丟包

「接收方」發(fā)給「發(fā)送方」的兩個 ACK 確認應答都丟失了,所以發(fā)送方超時后,重傳第一個數(shù)據(jù)包(3000 ~ 3499)。

于是「接收方」發(fā)現(xiàn)數(shù)據(jù)是重復收到的,于是回了一個 SACK = 3000~3500,告訴「發(fā)送方」3000~3500 的數(shù)據(jù)早已被接收了,因為 ACK 都到了 4000 了,已經(jīng)意味著 4000 之前的所有數(shù)據(jù)都已收到,所以這個 SACK 就代表著D-SACK。

這樣「發(fā)送方」就知道了,數(shù)據(jù)沒有丟,是「接收方」的 ACK 確認報文丟了。

例子2

數(shù)據(jù)包(1000~1499)被網(wǎng)絡延遲了,導致「發(fā)送方」沒有收到 Ack 1500 的確認報文。而后面報文到達的三個相同的 ACK 確認報文,就觸發(fā)了快速重傳機制,但是在重傳后,被延遲的數(shù)據(jù)包(1000~1499)又到了「接收方」;

所以「接收方」回了一個 SACK=1000~1500,因為 ACK 已經(jīng)到了 3000,所以這個 SACK 是 D-SACK,表示收到了重復的包。

這樣發(fā)送方就知道快速重傳觸發(fā)的原因不是發(fā)出去的包丟了,也不是因為回應的 ACK 包丟了,而是因為網(wǎng)絡延遲了。

D-SACK有這么幾個好處:

可以讓「發(fā)送方」知道,是發(fā)出去的包丟了,還是接收方回應的 ACK 包丟了;

2. 可以知道是不是「發(fā)送方」的數(shù)據(jù)包被網(wǎng)絡延遲了;

3. 可以知道網(wǎng)絡中是不是把「發(fā)送方」的數(shù)據(jù)包給復制了;

在 Linux 下可以通過net.ipv4.tcp_dsack參數(shù)開啟/關閉這個功能。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯(lián)系作者。

相關閱讀更多精彩內容

友情鏈接更多精彩內容