1、背景介紹
TCP使用可靠的傳輸協(xié)議,即意味著必須按序、無差錯的傳送數(shù)據(jù)到目的端,那么如果在傳輸過程中發(fā)送的包丟失了該怎么辦?TCP的重傳機制就是:如果發(fā)送方認為發(fā)生了丟包現(xiàn)象就重發(fā)這些數(shù)據(jù)包。顯然,我們需要一個方法去猜測是否發(fā)生了丟包。最簡單的想法就是,接收方每接收到一個包就向發(fā)送者返回一個ACK,表示自己已經(jīng)收到了這段數(shù)據(jù),反過來,如果發(fā)送方一段時間內(nèi)沒有收到ACK,就知道很可能是數(shù)據(jù)包丟失了,緊接著就重發(fā)該數(shù)據(jù)包,直到收到ACK為止。
為什么是猜測呢? 因為即使是超時了,這個數(shù)據(jù)包也可能并沒有丟,它只是繞了段遠程,來的很晚而已。畢竟TCP協(xié)議是位于傳輸層的協(xié)議,不可能明確知道數(shù)據(jù)鏈路層和物理層發(fā)生了什么。但是這并不妨礙我們的超時重傳機制,因為接收方會自動忽略收到的重復(fù)的包。
下面我們具體講一講TCP的重傳機制:
2、重傳--TCP的重要事件
(1)基于計時器的重傳---超時重傳
這種機制下,每個數(shù)據(jù)包都有相應(yīng)的計時器,當(dāng)超過指定的時間后,沒有收到對方的 ACK 確認應(yīng)答報文就會重發(fā)該數(shù)據(jù)包。

超時時間應(yīng)該設(shè)置為多少
我們先來了解一下 RTT (Round-Trip Time 往返時延)

而超時時間是以 RTO(Retransmission Timeout 超時重傳時間)表示。
超時時間不宜設(shè)置的過長或過短,否則:

綜上可知,RTO設(shè)置的值應(yīng)該略大于RTT的值。

RTO值的計算:
https://blog.csdn.net/JXH_123/article/details/27345151
值得注意的是:每觸發(fā)一次超時重傳,都會將下一次超時時間間隔設(shè)為先前值的兩倍。遇到超時說明網(wǎng)絡(luò)環(huán)境差,不宜頻繁發(fā)送。
Wireshark 抓包顯示:

超時重傳存在的問題是:
?當(dāng)一個報文段丟失時,會等待一定的超時時間后才重傳,增加了端到端的時延;
?當(dāng)一個報文段丟失時,在其等待超時的過程中,可能會出現(xiàn)這種情況: 其后的報文段已經(jīng)被接收端接收但卻遲遲得不到確認,發(fā)送端就也以為丟失了,從而引起不必要的重傳,既浪費時間也浪費資源。(例如: 數(shù)據(jù)包5丟失,數(shù)據(jù)包6、7、8、9都已到達接收方,這個時候客戶端只能等服務(wù)端發(fā)送ACK,因此對于客戶端來說,它完全不知道丟了幾個包,可能就悲觀的認為:5后面的數(shù)據(jù)包都丟了,就重傳這5個數(shù)據(jù)包,這就比較浪費了)。
(2)基于接收方的反饋信息的重傳---快速重傳
剛剛提到過,基于計時器的重傳往往要等待很長時間,而快速重傳使用了很巧妙的方法來解決這個問題。
快速重傳(Fast Retransmit)機制不以時間為驅(qū)動,而是以數(shù)據(jù)為驅(qū)動重傳。
由于TCP采用的是累計確認機制,當(dāng)接收端收到比期望序號大的報文段時,便會重復(fù)發(fā)送最近一次確認的報文段的確認號,即 冗余 ACK (Duplicate ACK)。

這樣,如果在超時重傳定時器溢出之前,接收到連續(xù)的三個重復(fù)冗余 ACK (第一個ACK是正常的,后三個是冗余的),發(fā)送端便知曉哪個報文段在傳輸過程中丟失了,于是重發(fā)該報文段,而不需要等待超時重傳定時器溢出,大大提高了效率。
Wireshark 抓包顯示:

但是,快速重傳仍然沒有解決第二個問題:到底該重傳多少個包?
(3)帶選擇確認的重傳---SACK
改進的方法就是 SACK (Selective Acknowledgment),簡單來說就是在快速重傳的基礎(chǔ)上,返回最近收到的報文段的序列號范圍,這樣客戶端就知道,哪些數(shù)據(jù)包已經(jīng)到達服務(wù)器了。
看下例子:

存在 SACK 選項時
當(dāng)500-599報文到達,接收方發(fā)送? ACK 200? ,SACK [500,600)
當(dāng)600-699報文到達,接收方發(fā)送? ACK 200? ,SACK [500,700)
當(dāng)700-799報文到達
當(dāng)800-899報文到達
當(dāng)900-999報文到達,接收方累積確認發(fā)送? ACK 200? ,SACK [500,1000)
連續(xù)收到3個重復(fù)ACK,發(fā)送方經(jīng)檢查發(fā)現(xiàn)200-499的數(shù)據(jù)丟失了,執(zhí)行快速重傳,待接收方接收到200-499的數(shù)據(jù),并返回 ACK 1000時,發(fā)送方的所有數(shù)據(jù)均已確認完畢,移動滑動窗口到1000位置處。
使用 SACK可以告知發(fā)送方 收到了哪些數(shù)據(jù),發(fā)送方收到這些消息后就會知道哪些數(shù)據(jù)丟失,然后立即重傳丟失的部分。
需要注意的是:只有收到失序的分組時才可能會發(fā)送SACK。
SACK 包括了兩個TCP選項,一個選項用于標識是否支持 SACK(SACK_Permitted),在TCP建立連接時發(fā)送;另一種選項則包含了具體的 SACK信息。
(1)SACK_Permitted 選項

該選項只允許在TCP連接建立時,有 SYN標志的包中設(shè)置,在連接建立階段,主動發(fā)起連接的一方在它的SYN中指定選項。只有在它從另一方的SYN中收到了這個選項之后,SACK機制才會被使能。

(2)SACK 信息選項

SACK 選項參數(shù)告訴對方已經(jīng)接收到并緩存的不連續(xù)的數(shù)據(jù)塊,發(fā)送方可據(jù)此信息檢查究竟是哪個塊丟失,從而發(fā)送相應(yīng)的數(shù)據(jù)塊。

?Left Edge:本區(qū)塊的第一個序號。 Right Edge:本區(qū)塊的最后序號的下一個序號。
[Left Edge, Right Edge)區(qū)間的ACK 序號表示本次確認收到的序號。
問題1:SACK選項最多能包含多少個需重傳的塊?
?????? 由于TCP首部的最大長度為 60 byte,而固定首部占用了 20 byte,對于SACK選項本身占用了2 byte,所以剩下 60-20-2=38 byte。而每個塊(包括開始和結(jié)束)占用 8 byte,所以最多可標識的塊數(shù)為 38/8 = 4塊,所以 SACK 最多可以包括4個需重傳的塊。同時由于SACK有些時候會和時間戳(占10字節(jié))一起用,因此,此種情況下最多只有3個SACK。
問題2:SACK選項的使用規(guī)則是怎么樣的?
SACK 的發(fā)送方,即 報文的接收端
第一個塊需要指出是哪一個到達的報文觸發(fā)的 SACK
盡可能多的把所有的塊填滿
SACK 要報告最近接收的不連續(xù)的數(shù)據(jù)塊
SACK 的接收端,即 報文的發(fā)送端:
數(shù)據(jù)沒有被確認前,都會保持在滑動窗口內(nèi)
每個數(shù)據(jù)包都有一個 SACKed 的標志,對于已經(jīng)標示的報文,再次接收到時會忽略
?如果SACK丟失,超時重傳之后,重置所有數(shù)據(jù)包SACKed 標志
(4)帶重復(fù)選擇確認的重傳---DSACK
DSACK是在SACK的基礎(chǔ)上做了一些擴展,主要用于對收到的重復(fù)報文進行了處理。
它的主要作用是:告訴發(fā)送方有哪些數(shù)據(jù)被重復(fù)接收了。
DSACK同樣使用了與SACK一樣的報文格式,唯一區(qū)別在于:第一個連續(xù)的block指定的是觸發(fā)DSACK的重復(fù)報文的序號空間。如果第一個段的范圍被ACK范圍所覆蓋,那么就是DSACK。或者,第一個段的范圍被SACK的第二個段覆蓋,那么就是DSACK。


引入DSACK的好處有:
1)可以讓發(fā)送方知道,是發(fā)出去的包丟了,還是回來的ACK包丟了;

2)是不是自己的 timeout 設(shè)置太小了,導(dǎo)致重傳;

3)網(wǎng)絡(luò)上出現(xiàn)了先發(fā)的包后到的情況(又稱數(shù)據(jù)包失序);

4)網(wǎng)絡(luò)上是不是把我的數(shù)據(jù)包給復(fù)制了;

總之,DSACK的目的是幫助發(fā)送方判斷,是否發(fā)生了包失序、ACK丟失、包重復(fù)或偽重傳,讓TCP可以更好的做網(wǎng)絡(luò)流量控制。
3、總結(jié)
超時重傳機制能解決數(shù)據(jù)包丟失的問題,但是超時重傳機制存在等待時間太長,浪費時間在等待上,降低了傳輸效率和無法知道需要重傳哪些數(shù)據(jù)包的問題。 快速重傳能解決超時重傳的等待時間太長的問題,但是對于究竟該重傳哪些包的問題仍然不能有效解決。SACK能需要重傳哪些數(shù)據(jù)包的問題,它可以知道哪些包是被確認接收的,客戶端能據(jù)此判斷需要重傳的包。DSACK則是作為SACK的一個輔助措施,可以用來判斷網(wǎng)絡(luò)究竟是出現(xiàn)了什么情況,據(jù)此做好網(wǎng)絡(luò)流量控制。