傳輸層:TCP 可靠資料傳輸
禁止碼迷,布布扣,豌豆代理,碼農教程,愛碼網等第三方爬蟲網站爬取!
目錄
序號和確認號
TCP 報文中最重要的 2 個欄位是序號和確認號,這兩個欄位是 TCP 實現可靠資料傳輸的關鍵。TCP 認為資料是無結構、有序的位元組流,序號是建立在傳送的位元組流上,而不是建立在傳送的報文段上,因此序號是一個報文段的首位元組的位元組流編號。接下來講解確認號,報文段的確認號是源主機希望從目的主機收到的下一位元組的序號。由於 TCP 僅確認流中第一個丟失位元組之前的位元組,該位元組對應的序號之前的所有位元組都被收到,因此 TCP 的確認方式也被稱之為累積確認
在實際情況下,一條 TCP 連線的雙方均可隨機地選擇初始序號,這麼做可以減少存在於兩臺主機先前已終止的連線中的殘餘報文段被傳輸的可能性。當接收方收到序號是亂序的報文段時應該怎麼處理?TCP 標準中並沒有明確地規定,所以這個問題將交給程式設計師來解決。實踐中接收方會保留失序的位元組,並且等待缺少的位元組來填補完整。
往返時間和超時重傳
TCP 使用超時/重傳機制來處理丟包問題,每傳送一個報文段,就對這個報文段設定一次計時器。只要計時器設定的重傳時間到但還沒有收到確認,就要重傳這一報文段。如果把超時重傳時間設定得太短,就會引起很多報文段的不必要的重傳,使網路負荷增大。但若把超時重傳時間設定得過長,則又使網路的空閒時間增大,降低了傳輸效率。合適的重傳時間應該如何設定?
往返時間估計
計時器的超時間隔設定是個困難的問題,首先超時時間間隔必須大於該連結的往返時間(RTT)。TCP 採用了一種自適應演算法,通過 RTT 樣本計算出加權平均往返時間 RTTs。計算方式為第一次測量到 RTT 樣本時,RTTS 值就取為所測量到的 RTT 樣本值。以後每測量到一個新的 RTT 樣本,就按下式重新計算一次 RTTS:
式中 0 ≤ a ≤ 1,若 a 很接近於零表示 RTT 值更新較慢,若選擇 a 接近於 1 則表示 RTT 值更新較快。RFC 2988 推薦的 a 值為 1/8,即 0.125。
超時重傳時間
RTO (Retransmission Time-Out) 應略大於上面得出的加權平均往返時間 RTTs,RFC 2988 建議使用下式計算 RTO:
RTTd 是 RTT 的偏差的加權平均值,RFC 2988 建議第一次測量時,RTTd 值取為測量到的 RTT 樣本值的一半。在以後的測量中,則使用下式計算加權平均的 RTTd:
此處需要強調的是,對重傳分組的 ACK 計算 RTT 會導致 RTTs 和 RTO 資料變得不夠準確,將會與符合現實情況的 RTO 產生較大的偏差。因此在計算平均往返時間 RTT 時,只要報文段重傳,就不採用其往返時間樣本
可靠資料傳輸
TCP 在 IP 不可靠的盡力而為的服務之上,建立了可靠資料傳輸服務。我們原來的想法是每一個已傳送但並未確認的報文段都配備一個定時器,但是定時器的管理需要巨大的開銷。因此即使有多個已傳送但是未確認的報文段,還是僅使用單一的重傳定時器。
TCP 傳送方
TCP 傳送方有 3 個與傳送和重傳有關的主要事件:從上層應用程式收到資料、定時器超時、收到 ACK。TCP 從應用程式接收資料,將資料封裝在一個報文段中,並把資料交給 IP 協議。每一個報文段都包含一個序號,序號是該報文段第一個資料位元組的位元組流編號。若定時器還沒有為某些其他報文段而執行,則報文段傳到 IP 時 TCP 就啟動定時器。
第二個事件是超時,TCP 通過重傳引起超時的報文段來響應超時事件,然後 TCP 重啟定時器。
第三,當傳送方收到一個包含有效 ACK 欄位值的報文段,則 TCP 將 ACK 的值和最早未被確認位元組的序號(SendBase)進行對比。TCP 採用累積確認,所以確認號確認了在確認號之前的所有位元組都已經收到。如果確認號數值大於 SendBase,則該 ACK 在確認一個或多個先前未被確認的報文段,傳送方就據此更新 SendBase。若當前還有未被確認的報文段,TCP 還需要重啟計時器。
虛擬碼
NextSeqNum = InitialSepNumber;
SendBase = InitialSepNumber;
while(1)
{
switch 事件:
{
case 從應用程式接收到資料:
生成具有序號 NextSeqNum 的 TCP 報文段;
if(定時器當前沒有執行)
啟動定時器;
向 IP 協議傳遞報文段;
NextSeqNum = NextSeqNum + length(data); //更新序列號
break;
case 定時器超時:
重傳具有最小序號但仍未應答的報文段;
重啟定時器;
break;
case 收到 ACK,ACK 欄位值為 y:
if(y > SendBase)
{
SendBase = y;
if(當前仍無任何應答報文段)
啟動定時器;
}
break;
}
}
情景分析
首先看第一個情景,傳送方向接收方傳送一個報文段,但是接收方傳送的 ACK 發生了丟包。此時傳送方將重傳分組,當接收方發現該分組曾經收到過,因此接收方就會丟棄重傳報文段中的這些位元組。
第二個情景,傳送方向接收方傳送一個報文段,但是接收方傳送的 ACK 都延遲到達傳送方。由於超時時間發生,傳送方重傳第一個分組,同時重啟定時器。接收方收到重複的報文段時,傳送的還是第二個報文段的 ACK。只要第二個報文段的 ACK 在新的超時發生前到達,就不會被重傳。
第三個情景,傳送方向接收方傳送一個報文段,但是接收方傳送的第一個報文段的 ACK 延遲到達傳送方。但是在超時事件發生前,傳送方若接收到了第二個報文段的 ACK,則說明第一個報文段也被收到了,就不會進行重傳。
超時間隔加倍
接下來考慮一個新的問題,當報文段的時延突然增大了很多時,在原來得出的重傳時間內不會收到確認報文段。於是就重傳報文段。但是在計算超時間隔時間時,不考慮重傳的報文段的往返時間樣本,這樣超時重傳時間就無法更新。因此 TCP 在實際中實現時,報文段每重傳一次,就把 RTO 增大一些,係數 γ 的典型值是 2。當不再發生報文段的重傳時,才根據報文段的往返時延更新平均往返時延 RTT 和超時重傳時間 RTO 的數值。
這種修改提供了一種形式受限的擁塞控制,因為在擁塞的時候持續地重傳分組,會使得擁塞更為嚴重。
快速重傳
超時重傳可能會引發超市週期相對較長的問題,也就是重發丟失分組前要等很長時間。若某個分組發生丟包,可能發生多個重複的 ACK,這些冗餘 ACK 可以用於檢測丟包事件的發生。
接收方動作
事件 | TCP 接收方的動作 |
---|---|
期望序號的報文按序到達,且之前資料都已確認 | 延遲的 ACK,對下一個報文段等待 500 ms,若沒到達則傳送 ACK |
期望序號的報文按序到達,另一個按序報文段等待 ACK 傳輸 | 立即傳送單個累積 ACK,確認 2 個按序報文段 |
比期望序號大的失序報文段到達 | 立即傳送冗餘 ACK,指示下一個期待位元組的序號 |
能部分或完整填充接收資料建個的報文段到達 | 倘若該報文段起始於間隔的低端,則立即傳送 ACK |
傳送方動作
傳送方若接收到對相同資料的 3 個冗餘 ACK,則說明可能跟在這個已被確認 3 次的報文段之後的報文段發生了丟包。此時 TCP 將執行快速重傳,即在該報文段的定時器過期之前重傳丟失的報文段。
case 收到 ACK,ACK 欄位值為 y:
if(y > SendBase)
{
SendBase = y;
if(當前仍無任何應答報文段)
啟動定時器;
}
else
{
y 對應的冗餘 ACK 數 += 1;
if (冗餘 ACK 數 == 3) //快速重傳
重新發送具有序號 y 的報文段;
}
break;
回退 N 步還是選擇重傳?
考慮一個問題,TCP 是一個 GBN 協議還是 SR 協議?TCP 使用的是累積確認,正確但是失序的報文段不會被接收方逐個確認,這個特性使得 TCP 看上去是一個 GBN 協議。考慮一個情景,當傳送方傳送多個報文段,第二個資料段傳送丟包造成失序,但是後續的所有報文段都被成功接收。但是由於累積確認,後續的報文段的 ACK 都會要求傳送方傳輸第二個報文段,這就可能導致大量的不必要重傳。
對 TCP 提出的一種修改意見就是選擇確認,它允許 TCP 接收方有選擇地確認失序報文段。接收方收到了和前面的位元組流不連續的兩個位元組塊,如果這些位元組的序號都在接收視窗之內,那麼接收方就先收下這些資料,但要把這些資訊準確地告訴傳送方,使傳送方不要再重複傳送這些已收到的資料。
當選擇確認機制和選擇重傳機制結合起來使用時,TCP 也會看起來像是 SR 協議。因此 TCP 的差錯恢復機制,可以被認為是 GBN 協議和 SR 協議的結合體。
參考資料
《計算機網路(第七版)》 謝希仁 著,電子工業出版社
《計算機網路 自頂向下方法》 [美] James F.Kurose,Keith W.Ross 著,陳鳴 譯,機械工業出版社