下層網絡層(IP)可能會出現丟失,重複或則失序包的狀況,TCP協議須要提供可靠的數據傳輸服務.爲保證數據傳輸的正確性,TCP重傳其認爲已經丟失的包.TCP根據接收端返回至發送端的一系列確認信息來判斷是否丟包.當數據段或確認信息失敗,TCP啓動重傳操做,重傳還沒有確認的數據.重傳的兩套機制:1.基於時間,2.基於確認信息的構成(更高效)linux
1.RTO(retransmission timeout,重傳超時):tcp在發送數據的時候設置一個計時器,若超時未收到數據確認信息,則會引起相應的超時或則基於計時器的的重傳操做,這種計時器超時稱爲RTO(重傳超時)算法
2.fast transmission(快速重傳):若TCP累計確認沒法返回新的ACK,或則當ACK包含的確認信息(SACK,selective acknowledge)代表出現失序報文段時,快速重傳會推斷出現丟包.緩存
binary exponential backoff(二進制指數退避):每次重傳間隔時間markdown
邏輯上講,TCP擁有兩個閥值來決定如何重傳一個報文段.R1和R2網絡
R1:TCP在向IP層傳遞"消極建議"(如從新評估當期那的IP路徑)前,願意嘗試重傳的次數(或則等待時間);數據傳輸段(SYN報文段)的R1>=3tcp
R2:(R2>R1)指示TCP應該放棄當前鏈接的時機.數據傳輸段的R2>=100s,TCP創建鏈接段的R2>=180s性能
linux中的R1和R2的設置,經過應用程序或則系統配置.參數 net.ipv4.tcp_retries1:重傳次數,默認爲3, net.ipv4.tcp_retries2:默認15,對應約爲13~30分鐘,根據具體的RTO而定, 對於SYN報文段net.ipv4.tcp_syn_retries和net.ipv4.tcp_synack_retries限定了重傳次數,默認爲5(約180s)優化
TCP怎樣根據給定鏈接的RTT設置RTO?若TCP先於RTT重傳,可能會在網絡中引入沒必要要的重複數據,反之,若延遲至大於RTT的間隔發送數據,總體網絡利用率就會降低.spa
TCP在收到數據以後會返回確認信息,所以可在該信息中攜帶一個字節的數據來測量傳輸該確認信息所須要的時間.每一個測量結果稱爲RTT樣本(RTT sample).TCP首先須要根據一段時間內的樣本值創建好估值,第二部是怎樣根據估值設置RTO.code
最初的TCP規範[RFC0793]採用如下公式計算獲得平滑的RTT估值(稱爲SRTT):
SRTT <--- α(SRTT) + (1 - α)RTTs
考慮到SSRTT估計器的獲得的估計值會隨RTT爲變化,[RFC0793]推薦根據以下公式設置RTO:
RTO = min(ubound,max(lbound,(SRTT)β))
咱們稱爲經典方法,他使得RTO的值設置爲1秒,或約爲兩倍的SRTT
按照上述經典方法設置計時器,將沒法適應RTT大規模變更(特別是當實際的RTT遠大於估計值,會致使沒必要要的重傳).增大的RTT樣本代表網絡出現了過載,此時沒必要要的重傳會進一步加劇網絡的負擔.
爲解決這一問題,可對原方法改進一適應RTT變更較大的狀況.記錄RTT測量值的變化狀況以及平均值老獲得較爲準確的估計值.基於均值和估計值的變化來設置RTO,將比使用均值的常數倍來計算RTO更能適應RTT變化幅度較大的狀況.
以下的算法採用了經典方法([RFC0793]),還同時考慮到了RTT樣本變化值的方法計算RTO的對比狀況.咱們將TCP獲得的RTT測量樣本值做爲一統計過程,同時測量平均值和方差(或則標準差)能更好的估計未來值,還能夠幫助TCP設置一個能適應大多數狀況的RTO值.
平均誤差(mean deviation)是對標準差的一種好的逼近,可是計算更容易,更快捷.這是由於計算標準差須要對方差的進行平方根運算,對於快速TCP實現來講代價較大.所以咱們結合了平均值和平均誤差來進行估值.對每一個RTT測量值M(前面的RTTs)採用如下公式:
srtt <--- (1 - g)(srtt) + (g)M
rttvar <--- (1 - h)(rttvar) + (h)(|M - srtt|)
RTO = srtt + 4(rttvar)
這裏,srtt代替了以前的SRTT,而且rttvar爲平均誤差的EWMA,而非採用以前的β來設置RTO.上述等式也能夠寫成另一種形式,對計算機實現來講較爲方便:
Err = M - srtt
srtt <--- srtt + g(Err)
rttvar <--- rttvar + h(|Err| - rttvar)
RTO = srtt + 4(rttvar)
當RTT變化時,誤差的增量越大,RTO增加越快.
經典方法和Jacobson計算方法的區別:
在測量RTT的過程彙總,TCP時鐘時鐘處於運行狀態.對於初始序列號碼來講,實際的TCP鏈接的時鐘並不是從零開始,也沒有絕對精確的精度,TCP的時鐘一般爲某個變量,該變量隨着系統時鐘而作出更新,但並不是一對一地同步更新.TCP時鐘一個"滴答"的時間一般稱爲粒度.一般,該值相對較大(約500ms),但新的時鐘粒度更細(linux採用了1ms)
粒度會影響RTT的測量值以及RTO的設置.粒度用於優化RTO的更新狀況,並給RTO設置了一個下界.計算公司以下:
RTO = max(srtt + max(G,4(rttvar)),1000)
在首個SYN交換以前,除非系統提供,不然TCP沒法設置RTO初始值.根據[RFC6298],RTO的初始值爲1s,而初始SYN報文段採用的超時時間爲3s.當收到首個RTT測量結果M,估計器按照如下方式進行初始化:
srtt <--- M
rttvar <--- M/2
重傳二義性:在測量RTT樣本的過程當中若一個包的傳輸出現超時,該數據就會被重傳,接着收到一條ACK信息,那麼該ACK是對第一條仍是第二條傳輸的確認就存在這二義性.
Karn算法
[KP87]指出,出現超時,接收到重傳數據的確認信息時不能更新RTT估計值.這是Karn算法的第一部分.經過排除二義性數據來解決RTT估計值出現的二義性問題.
TCP在計算RTO的過程當中採用一個退避係數(backoff factor),每當重傳計時器出現超時,退避係數加倍,該過程一直持續到非重傳數據.此時退避係數爲1,重傳計時器返回正常值.對重傳過程退避係數加倍,這是Karn算法的第二部分.若TCP超時,同時會引起擁塞控制機制,以此改變發送速率.[KP89]中所述:
當接收到重複傳輸(即至少重傳一次)數據的確認信息時,不進行該數據包的RTT測量,能夠避免重傳二義性問題.另外,對該數據以後的包採起退避策略.僅當接受到未經重傳的數據時.該SRTT才用於計算RTO.
TCP時間戳選項(TSOPT)做爲PAWS算法的基礎,還能夠做RTT測量(RTTM).容許發送者在返回的對應確認信息中攜帶一個32比特的數.
時間戳值(TSV)攜帶初始SYN的TSOPT中,並在SYN+ACK的TSOPT的TSER部分返回,以此設定srtt,rttvar與RTO的初始值.
當傳輸大批量數據時,TCP一般採起每兩個報文段返回一個ack的方法,當數據出現丟失,失序或則重傳成功時,TCP的累計確認機制報文段與其ACK之間的不是一一對應關係.爲了解決這些問題,使用時間戳選項的TCP採用如下算法來測量RTT樣本值:
RTO一般設置爲srtt+4(rttvar),不管最大RTT樣本的值是大於仍是小於srtt,rttvar的任何大的變化,都會致使RTO的增大.Linux經過減少RTT樣本值大幅減少rttvar的影響來解決這一問題.Linux設置RTO的方法:
與標準方法同樣,Linux也記錄srtt和rttvar值,但還同時記錄兩個新的變量mdev和mdev_max.
Linux根據mdev_max的值來更新rttvar.RTO老是等於srtt與4(rttvar)之和,以此保證RTO不超過TCP_RTO_MAX(默認值爲120s).以下圖.
首次RTT樣本測量
srtt = 16ms
mdev = (16/2)ms = 8s
rttvar = mdev_max = max(mdev,TCP_RTO_MIN) = max(8,50)
RTO = srtt + 4(rttvar) = 16 + 4(50) = 216ms
在初始SYN交換以後,發送端對接收端的SYN返回一個ACK,接收端進行了一次窗口更新.這些包未包含數據數據(SYN或則FIN字段,但算做數據),而且沒有記錄相應的時間,而且發送端接收到窗口更新時也沒有進行RTT更新.TCP對不含數據的報文段不提供可靠性傳輸,意味着若出現丟包也不會重傳,所以無需設定重傳計時器.
注意:TCP選項自己並不進行重傳或可靠性傳輸.僅僅當數據段(包含SYN和FIN報文段)中明確設定,纔會丟失重傳,但也僅做爲反作用.
當應用首次執行寫操做,發送端TCP發送兩個報文段,每一個報文段包含一個值爲127的TSV.因爲兩次發送間隔小於1ms(發送端的TCP發送粒度),所以兩個值相等.當發送端以這種方式發送多個報文段時,能夠看到之中並無前進的狀況.
接收端變量LastACK記錄上一次發送的ACK的序列號碼.本例中,上一個發送的ACK爲鏈接創建階段的SYN + ACK包,所以AC從1開始.首個全長(full-size)報文段到達,其序列號與LastACK吻合,則將TsRecent變量更新爲接收分組的TSV,即127.第二個報文段的到達並無更新TsRecent,由於其序列號碼字段與LastACK不匹配.接收端返回對應分組的ACK時,需在其TSER部分包含TsRecent,同時接收端還要更新LastACk變量的ACK號爲2801.
樣本m = 223 - 127 = 96
mdev = mdev(3/4) + |m - srtt|(1/4) = 8(3/4) + |80|(1/4) = 26ms
mdev_max = max(mdev_max,mdev) = max(50,26) = 50ms
srtt = srtt(7/8) + m(1/8) = 16(7/8) + 96(1/8) = 14 + 12 = 26ms
rttvar = mdev_max = 50ms
RTO = srtt + 4(rttvar) = 26 + 4(50) = 226ms
標準方法中rttvar所佔權重較大(係數爲4),所以當RTT減少時,也會致使RTO增加.在時鐘粒度較大時(500ms),不會有太大的影響,由於RTO可用值不多.可是,若時鐘顆粒較細,好比Linux的1ms,可能出問題.針對RTT減少的狀況,若新樣本值小於RTT估值範圍的下界(srtt - mdev),則減少樣本的權重.僞代碼以下:
if(m < (srtt - mdev))
mdev = (31/32) * mdev + (1/32)*|srtt -m|
else
mdev = (3/4)* mdev + (1/4) * |srtt - m|
複製代碼
快速重傳機制基於接收端的反饋信息來引起重傳,而非重傳計時器的超時.與超時重傳相比,快速重傳能更加及時有效地修復丟包狀況.
快速重傳算法:TCP發送端在觀測到至少dupthresh(重複ACK的閥值)個重複ACK後,即重傳可能丟失的數據分組,而沒必要等到重傳計時器超時.固然也能夠同時發送新數據.根據重複ACK推斷的丟包一般與網絡擁塞有關,所以伴隨快速重傳應促發擁塞控制機制.不採用SACK時,在接受到有效ACK前至多隻能重傳一個報文段.採用SACK,ACK可包含額外信息,使得發送端在每一個RTT時間內能夠填補多個空缺.
隨着選擇確認選擇的標準化,TCP接收端可提供SACK功能,經過TCP頭部累計的ACk字段來描述描述其接受到的數據.ACK號與接收端緩衝中的其餘數據之間的間隔稱爲空缺.序列號高於空缺的數據稱爲失序數據,由於這些數據和以前接受的序列號碼不連續.
TCP發送端的任務是經過重傳丟失的數據來填補接收端緩衝中的空缺,但同時也要儘量保證不重傳正確接收到的數據.合理採用SACK信息可以更快地實現空缺填補,且可以較少沒必要要的重傳,緣由在於其在一個RTT內可以獲知過個空缺.當採用SACK選項時,一個ACK可包含三四個告知失序數據的SACK信息.每一個SACK信息包含32位的序列號,表明接收端存儲的失序數據的起始至最後一個序列號.
SACK選項指定n個塊的長度爲8n+2字節,所以40字節可包含4個塊.一般SACK會與TSOPT一同使用,所以須要額外的10個字節(外加2字節的填充數據),這意味着SACK在每一個ACK中只能包含3個塊.
接收端在TCP鏈接創建期間收到SACK許可選項便可生成SACK.一般來講,每當緩存中存在失序數據時候,接收端就能夠生成SACK.
第一個SACk塊內包含的是最近接收到的報文段的序列號碼.因爲SACK選項的空間有限,應儘量確保向TCP發送端提供最新的信息.其他的SACK塊包含的內容也按照接收到的前後依次排列.也就是說,最新的一個塊包含的內容除了包含接受到的序列號,還須要重複以前的SACK塊.
在一個SACK選項中包含多個SACK塊,而且在多個SACK中重複這些塊信息的目的是爲了防止SACK丟失提供一些備份.
在發送端也應該提供SACK功能,而且合理地利用接收到的SACK塊來進行丟包重傳,該過程稱爲選擇性重傳(selective retransmission) 或選擇性重發(selective repeat).SACk發送端記錄接收到的累計ACK信息,還須要記錄接收到的SACK信息,並利用該信息來避免重傳正確接受的數據.
當SACk發送端執行重傳時,一般是因爲接收到了SACK或則重複的ACK,它能夠選擇發送新數據或則舊數據.SACK信息提供接受端數據的序號範圍,所以發送端可據此推斷須要重傳的空缺數據.最簡單的辦法是使發送端首先填補接收端的空缺,而後再繼續發送新數據.這是經常使用的方法.
在不少狀況下,即便沒有出現數據的丟失也可能引起重傳.這種沒必要要的重傳稱爲僞重傳(spurious retransmission),主要緣由是僞超時過早判斷超時,其餘緣由包括失序,包重複,或則ACK丟失.在實際RTT顯著增加,超過當前RTO時,可能出現僞超時.在下層協議性能變化較大的環境中(無線環境),這種狀況出現的比較多.
僞超時的解決方法,一般包含檢測算法(detection)與響應算法(response)
實驗性質的Eifel檢測算法利用了TCP的TSOPT來檢測僞重傳.在發生超時重傳後,Eifel算法等待接收下一個ACK,若對第一次傳輸的確認,則判斷該重傳是僞重傳.
Eifel檢測算法的機制很簡單.它須要使用TCP的TSOPT.當發送一個重傳後,保存器TSV值.當接收到的相應分組的ACK後,檢測該ACK的TSER部分.若TSER部分小於以前的存儲的TSV值,則可判斷該ACK對應的是原始的傳輸分組,即該分組是僞重傳.
**前移RTO恢復(Forward-RTO Recovery,F-RTO)**是檢測僞重傳的標準算法.該算法只檢測由重傳計時器超時引起的僞重傳;對於其餘緣由引起的僞重傳則沒法判斷.
F-RTO會修改TCP的行爲,在超時重傳以後接受到的第一個ACK時,TCP會發送新(非重傳)數據,以後再響應一個到達的ACK.若是其中有一個重複ACK,則認爲這次重傳沒有問題. 若是這兩個都不是重複ACK,則表示該重傳是僞重傳.若是新數據的傳輸獲得了相應的ACK,就使得接收端窗口前移.若是新數據的發送致使了重複的ACK那麼接收端至少有一個或更多的空缺.這兩種狀況,接收新數據都不會影響總體數據的傳輸性能.
一旦判斷出現僞重傳,則會引起一套標準操做,即爲Eifel響應算法.在重傳計時器超時後,它會檢查srtt和rttvar的值,並按照以下的方式記錄新的變量srtt_prev和rttvar_prev:
srtt_prev = srtt + 2(G)
rttvar_prev = rtvar
完成srtt_prev和rttvar_prev的存儲以後,就要觸發某種檢測算法.運行檢測算法以後可獲得一個特殊值,稱爲僞恢復(SpuriousRecovery).若是檢測到一次僞超時,則將僞恢復置爲SPUR_TO.若是檢測到遲超時,則將其置爲LATE_SPUR_TO.不然超時,改次超時爲正常超時,TCP繼續執行正常的響應行爲.
若爲僞恢復SPUR_TO,TCP可在恢復階段完成以前進行操做.將下一個要發送報文段的序列號修改成最新的未發送過報文段.這樣能夠避免"回退N"行爲.
若檢測到一次遲到僞超時,此時已生成對首次的重傳的ACK,則SND.NXT不改變.在以上兩種狀況中,都要從新設置擁塞控制狀態.而且一旦接收到重傳計時器超時後的發送的報文段ACk,就要按照以下方式更新srtt,rttvar和RTO
srtt <--- max(srtt_prev,m)
rttvar <---max(rttvar_prev,m/2)
RTO = srtt + max(G,4(rttvar))