一、介紹
在前面介紹thin stream時候我們介紹過有兩種場景下可能不會產生足夠的dup ACK來觸發快速重傳,一種是游戲類響應交互式tcp傳輸,另外一種是傳輸受到擁塞控制的限制,只能發送少量TCP報文.針對這種場景提出了一個快速重傳的改進算法即早期重傳(early retransmit,簡稱ER)。按照RFC5827,ER有兩種形式一種是基於字節的,一種是基於包的,基於包的ER精度要高於基於字節的ER,linux實現的是基於TCP包的ER,因此我們這里只介紹基於包的ER。
ER是在沒有新數據可以發送的場景下降低快速重傳dup ACK的門限,dup ACK是由亂序TCP報文觸發的,但是發出的總數據包的個數少於4個的時候,就會因為沒有足夠的dup ACK而不能觸發快速重傳(假設默認dup ACK門限是3)。發送端在接收到ACK時候如果要觸發快速重傳必須同時滿足下面兩個條件
發出去的但是還沒有收到ACK確認的TCP報文個數(假設為oseg)小於4
緩存中沒有未發送數據或者發送窗口受限不能發送新數據,如果允許發送新數據的話就可以進一步觸發dup ACK來達到門限了。
當滿足上面兩個條件時候,如果這個tcp連接未使能SACK的時候,用來觸發ER的dup ACK門限必須降低為
ER_thresh = oseg - 1
當這個TCP連接支持SACK的時候,觸發ER的條件則變為,(oseg-1)個TCP包已經被SACK確認。
二、linux實現
在linux實現上ER受到控制/proc/sys/net/ipv4/tcp_early_retrans控制,這個參數的描述如下
tcp_early_retrans - INTEGER
Enable Early Retransmit (ER), per RFC 5827. ER lowers the threshold
for triggering fast retransmit when the amount of outstanding data is
small and when no previously unsent data can be transmitted (such
that limited transmit could be used). Also controls the use of
Tail loss probe (TLP) that converts RTOs occurring due to tail
losses into fast recovery (draft-dukkipati-tcpm-tcp-loss-probe-01).
Possible values:
0 disables ER
1 enables ER
2 enables ER but delays fast recovery and fast retransmit
by a fourth of RTT. This mitigates connection falsely
recovers when network has a small degree of reordering
(less than 3 packets).
3 enables delayed ER and TLP.
4 enables TLP only.
Default: 3
其中TLP的內容我們后續介紹,暫時只關注前這個參數為0、1、2的場景,這個參數為0時候關閉ER,為1的時候使能ER,為2的時候使能ER,但是會延遲ER重傳(延遲時間為RTT/4),這樣可以在一定程度上減少網絡的冗余重傳。
從上面的描述中我們看到ER也是為了降低TCP的dup ACK門限,前面內容我們thin stream時候也介紹過一個參數tcp_thin_dupack也是用來降低dup ACK門限的,因此這個參數使能時候會與ER沖突,所以當tcp_thin_dupack使能的時候,不管tcp_early_retrans怎么設置都會關閉ER。linux同時還要求tcp_reordering為3的時候才會使能ER(默認值即為3)。
另外linux實現上還有一個點與協議有差異,上面我們介紹過TCP連接支持SACK的時候,觸發ER的條件則變為,(oseg-1)個TCP包已經被SACK確認。協議使用的是MUST來描述這個條件,但是linux實現上只要有任意一個TCP包被SACK就會觸發快速重傳。linux的這個實現是基於在TLP的協議草案中提出的增強ER。
三、wireshark示例
前4個示例我們使用與之前thin stream類似的流量模型來與之前的示例作對比(注意截圖中的RST消息產生的原因是server端緩存的TCP報文數據沒有被應用層讀取而直接close掉了,不用關注RST消息)
1、tcp_thin_dupack=0 tcp_thin_linear_timeouts=0 tcp_early_retrans=1緩存中沒有新數據待發送
這是示例與之前thin stream示例類似,不同之處在於第一次RTO超時發生了指數回退(原因是tcp_thin_linear_timeouts=0),RTO超時后server端寫入兩個TCP報文(No11和No12),兩個報文發出后client模式丟失No11報文,回復一個dup ACK包含17-25的SACK信息。此時緩存中沒有待發送的新數據,同時oseg = 2(No11和No12兩個報文),SACK了No12報文,滿足觸發ER的條件。因此觸發了No14的快速重傳,從這里我們可以看到設置tcp_early_retrans=1,tcp_thin_dupack=0時候和之前單獨設置tcp_thin_dupack=1的結果類似都會降低快速重傳dup ACK的門限,因此在設置了tcp_thin_dupack=1的時候就不會觸發ER重傳了。
2、tcp_thin_dupack=0 tcp_thin_linear_timeouts=0 tcp_early_retrans=1緩存中有新數據待發送
在設置tcp_thin_linear_timeouts=0后,從下圖中我們看到與第一次RTO超時重傳開始進行指數回退了(與之前的thin_stream對比)。接着server端寫入三個數據包,但是因為擁塞控制暫時只能發出兩個TCP報文(No11和No12),接着client回復了一個dup ACK帶有17-25的SACK信息,即模擬No11包丟失。可以看到此時並沒有觸發ER,原因是上面寫入了三個tcp報文只發送了兩個TCP報文,緩存中還有新數據可以發送。
3、tcp_thin_dupack=0 tcp_thin_linear_timeouts=0 tcp_early_retrans=2緩存中沒有新數據待發送
這個示例與第一個示例類似,不同的是這次設置tcp_early_retrans為2延遲觸發ER重傳。從server端程序中可以看到此時的RTT為0.44s,從下圖可以看到ER重傳的No14報文與No13報文之間的時間差大約也是0.11s,正好是RTT/4。其他地方與第一個示例一樣了
4、tcp_thin_dupack=0 tcp_thin_linear_timeouts=0 tcp_early_retrans=0緩存中沒有新數據待發送
接下來我們來看一下即沒有使能ER也沒有使能thin stream優化的場景,從下圖可以看到在收到No13的dup ACK時候並沒有觸發快速重傳,最終RTO超時重傳
5、tcp_thin_dupack=0 tcp_thin_linear_timeouts=0 tcp_early_retrans=1緩存中沒有新數據待發送
接下來我們看一下我們之前說的linux實現上對dup ACK門限的判斷與協議的差異性。在下圖中server端發出No6-No9四個TCP報文后,client端正常回退No6報文的ACK確認包。接着模擬No7報文丟失,對No8亂序報文響應No11確認包,其中包含17-25的SACK信息。可以看到測試oseg為3,按照協議需要SACK確認2個TCP數據包才會觸發ER,但是我們看到只需要No11通過SACK確認一個TCP報文(No8報文)就會觸發ER。其中No13是client對No9響應的ACK報文,包含17-33的SACK信息。
補充資料:
1、https://www.ietf.org/proceedings/69/slides/tcpm-9.pdf
2、https://datatracker.ietf.org/doc/rfc5827/