計算機網絡:運輸層(2)

一、TCP可靠傳輸原理

網絡層的IP協議僅做到盡最大努力交付。TCP協議必須採取一些措施才能使傳輸變得可靠。

1. 理想傳輸條件

理想傳輸條件下,不需要任何措施就可以實現可靠傳輸:

  1. 傳輸信道不產生差錯
  2. 無論發送方以多快的速度發送數據,接收方總是來得及處理收到的數據

實際的網絡不可能滿足這兩個條件。因此,TCP協議需要解決這兩個問題,以保證可靠傳輸。

2. 停止等待協議

全雙工信道的雙方既是發送方,也是接收方。因此將發送TCP報文的稱爲發送方,而接收報文並回送確認的稱爲接收方
停止等待協議就是每發送一個TCP分組後,等待對方回送確認,再發送下一個分組。
假設一個TCP連接中,端口A向端口B發送報文。下面分析停止等待協議中出現的情況:

  1. 全無差錯
    在全無差錯的情況下,A發送分組M1,發送完成後暫停發送,等待B收到分組後,回送確認;A在收到B發回的確認後,再發送下一個分組。
    無差錯情況
  2. 分組發送出現差錯
    分組出現差錯可以分爲分組丟失分組出錯兩種情況。無論哪種,B都不會回送確認。
    爲解決此問題,A中設有超時計時器,每當發送一個分組後,就要設置超時計時器開始計時。若在超時前收到了確認,即無差錯情況,則撤銷超時計時器;若在規定的時間內沒有收到確認,就將此前的報文重傳一次。這就是超時重傳機制
    超時重傳
    由此需要注意:
    ● 發送分組後,發送端需要暫時保留已發送的副本,留作重發使用。收到確認後纔將副本清除。
    ● 分組和確認分組需要進行編號,明確收到確認的分組和沒有收到確認的分組。
    ● 超時計時器的超時時間需要比平均往返時間稍長,具體的設置受大量不確定因素影響,將在實現部分討論。
  3. 確認出現差錯
    B中收到了分組,但是回送的確認出現了差錯。這裏可以分成兩種情況:
    確認丟失:發送的確認發生了丟失的情況。此時A認爲分組沒有成功發送,會重發一次。當B收到重發的分組後,做兩項操作:丟棄重複的分組重新向A發送確認
    確認丟失
    確認遲到:發送的確認由於網絡狀況的原因,確認在超時後纔到達。
    對於B而言,需要進行的操作不變;而對A而言,將會收到重複的確認。對於重複的確認,只需要丟棄即可

利用上面所述的確認重傳機制,可以實現在不可靠的傳輸網絡上進行可靠的通信
這樣的可靠傳輸協議常稱爲自動重傳請求

信道利用率
停止等待協議的優點是實現簡單,但缺點是信道利用率太低。
假設A和B之間有一條直通的信道傳輸分組:
信道利用率表示
A發送分組的時間是TD,B發送確認的時間是TA,往返時間爲RTT。忽略接收分組的處理時延和發送確認的等待時延,發送分組共需要的時間爲TD+RTT+TA。而其中只有TD的時間在傳送有用的數據,因此信道利用率可以表示爲:
U = TD / (TD+RTT+TA)
一般來說,TA << TD,因此信道利用率主要取決於RTT的大小,而RTT的大小則取決於信道特性。
當RTT >> TD時,信道的利用率就會非常低。如果出現差錯重傳,信道利用率會變得更低。

3. 連續ARQ協議

爲了提高傳輸效率,應該將低效率的停止等待協議更換爲流水線傳輸的方法。
流水線傳輸
爲了對流水線傳輸加以可靠控制,需要使用連續ARQ協議滑動窗口協議
滑動窗口協議較爲複雜,是TCP協議的精髓所在。發送方維持一個發送窗口,在發送窗口內的分組,都可以連續地進行發送,由此提高信道利用率。每當發送方收到一個分組的確認(應當是最早發送的),就將發送窗口向前滑動一個分組的位置。
滑動窗口
對接收方而言,需要採用累積確認的方式。接收方不必對每一個分組都發送確認,只需要對按序到達的最後一個分組發送確認,發送方收到該確認時,就認爲到該分組之前的分組均已經正確接收了。這種方式可以避免部分確認丟失造成的重傳損耗。
然而,累積確認帶來的問題是,不能正確地反映接收端接收分組的情況。例如當接收的分組未能按序到達,中間部分丟失時,接收端不能發送後半部分接收到分組的確認。因此,發送端在超時後,將會重發從第一個丟失的分組開始的所有分組,造成浪費。這種回退到未接收分組進行重傳的機制叫做回退N*(Go-Back-N)
由此可見,當網絡質量較差時,連續ARQ協議會造成負面影響

二、TCP報文的首部格式

TCP雖然是面向字節流的,但數據單元是報文段。一個TCP報文段分爲首部數據兩部分字段。TCP的全部功能都體現在它首部中的各字段中。下面是TCP報文段的首部格式:
TCP報文段首部
TCP報文段首部的前20個字節是固定的,後面4n字節是可根據需要而增加的選項。因此TCP報文首部的最小長度是20字節。
各字段意義如下:

  1. 源端口和目的端口:各佔2字節,用於TCP的複用和分用,與UDP相似。
  2. 序號:佔4字節,共232-1個序號,是本報文段發送的第一個字節在字節流中的位置(按序編號)。當序號用完後重新從0開始,即序號 = 字節流的編號 mod 232
  3. 確認號:佔4字節,是期望收到的下一個報文段的第一個數據字節的序號。若確認號爲N,表示N-1之前的字節都成功收到。
    一般來說,當下一次的序號重複時,已經過去了4GB的數據,舊的序號應該已經傳輸完成了。
  4. 數據偏移:佔4位,指出TCP報文段起始位置到數據部分起始位置的距離,因此實際上是首部長度。其單位爲32位4字節,最小爲5(20字節),最大爲15(60字節),即選項長度最大不超過40字節。
  5. 保留:佔6位,留作今後使用,目前置0。
  6. 控制位:下面有6個單位的控制位,指明本報文段的性質:
    URG:即緊急位。URG = 1時表示該報文緊急指針有效,通知系統此報文有緊急數據,應儘快處理。
    ACK:即確認位。ACK = 1時表示該報文確認號字段有效;否則無效。TCP規定建立連接後所有報文段的ACK都置1。
    PSH:即推送位。PSH = 1時表示收到該信息後立即發送或交付給進程,而不是等待到緩存填滿。
    RST:即重置位。RST = 1時表示TCP連接出現嚴重差錯,必須釋放連接,重新建立;或者用於拒絕非法報文或連接。
    SYN:即同步位。SYN = 1且ACK = 0時表示一個連接請求報文段;SYN = 1且ACK = 1表示接收連接請求。
    FIN:即終止位。FIN = 1時表明此報文的發送方數據已經發送完畢,要求釋放連接。
  7. 窗口 :佔2字節,是[0,28-1]的整數。該字段是本報文段發送方的接收窗口,作爲對方設置發送窗口的依據。
  8. 校驗和:佔2字節,與UDP類似,也需要加上僞首部。
  9. 緊急指針:佔2字節,指出報文段中緊急數據的字節數。在處理完緊急數據後,應用恢復到正常操作。當窗口爲0也可以發送緊急數據。
  10. 選項與填充:長度可變,最大40字節,且必須是4字節的整數倍。不足的部分將用0填充。

比較常見的選項字段:
● 最大報文段長度MSS:TCP報文段數據字段的最大長度。若TCP數據部分過短或過長都會降低傳輸效率,且路徑是動態變化的,因此很難確定最佳MSS,只能儘可能大,且不需要分片。
● 窗口擴大選項:佔3字節,用於擴大最大窗口。一個字節表示移位值S,新的窗口字段長度等於(16+S),S允許的最大值爲14,相當於窗口的最大值增大到230-1。
● 時間戳選項:佔10字節,主要是時間戳值字段時間戳送回字段(各4字節)。具有兩個功能:計算往返時間RTT防止序號繞回
選擇確認選項

三、TCP可靠傳輸實現

爲簡便討論,假定數據傳輸只在一個方向進行,即A發送數據,B接收並給出確認。

1. 以字節爲單位的滑動窗口

● 滑動窗口的工作

TCP的滑動窗口是以字節爲單位的。當A收到B發來的確認報文段,獲取其中的窗口確認號時,A就可以構造出自己的發送窗口。如:收到的窗口是20字節,確認號是31。
構造發送窗口
在沒有收到B的確認的情況下,A可以連續地將窗口內的所有數據全部發送出去。未收到確認的數據也要暫留在窗口中保存,以便於超時重傳。在不考慮擁塞等因素的前提下,窗口越大,傳輸效率就會越高。
窗口後沿後面的部分表示已經發送並且收到了確認,而前沿前面的部分表示目前不不允許發送,因爲接收方的接收窗口沒有爲其準備臨時存放的緩存空間。
發送窗口的位置由前沿後沿決定。窗口的後沿只能前移或不變,因爲不能撤銷已經收到的確認;窗口的前沿一般只能也只能前移和不變。前沿雖然可以收縮,但TCP標準強烈不贊成收縮,因爲窗口收縮可能會造成錯誤(已發送的分組被收縮)。
前沿和後沿並不總是一起移動的。可能後沿前移,但窗口縮小了,前沿不變;也可能後沿不變,窗口增大了,前沿前移。
狀態1
設發送窗口有三個指針p1,p2和p3。p1是後沿、p3是前沿、p2是已發送但未收到確認的最後一個分組序號。一個發送窗口可以由這三個指針表示:
p3 - p1 = A的發送窗口;p2 - p1 = 已發送但未接收確認的部分;p3 - p2 = 允許發送但未發送的部分(可用窗口)

而在B的接收窗口中,能夠接收31~50這20個字節的數據。當收到後沿所在字節的數據時,B可以發送新的確認報文段,更新確認號;如果數據未按序到達(即收到的數據不是後沿所在字節的數據),就不能更新確認號,而是再發送確認號爲31的報文。
狀態2
現在B收到了31、32、33這三個分組,根據累積確認,只需要發送確認號爲34的確認報文段。A收到確認報文後,將發送窗口的後沿向前移動;並且由於窗口沒有變化,前沿也向前移動。因此,A的發送窗口中,可用窗口擴大了,可發送的數據也變多了。
當滑動窗口的可用窗口爲0時,A不能再發送任何分組,必須等待B回送確認報文段。

● 窗口與緩存

TCP連接的雙方都有一個緩存。發送方的應用進程將數據寫入緩存等待發送,而接收方從緩存中取出數據交付給應用進程。
窗口與緩存
發送緩存用於存放已發送但未收到確認的數據、以及應用進程準備好發送的數據
發送窗口是發送緩存的一部分,其後沿和發送緩存重合(已確認的部分可以從緩存中刪除),而前沿小於等於發送緩存中最後的字節。前沿到緩存尾的部分就是不允許發送的部分。由於緩存是有限的,應用進程必須控制寫入緩存的速度不能過快,否則會沒有存放數據的空間。
接收緩存用於存放已收到但未讀取的數據、以及未按序到達的數據
接收窗口不能看作接收緩存的一部分,因爲接收緩存只存有已到達的數據;而接收窗口需要維護未到達數據的空間。
如果應用進程不能及時讀取數據,就會導致接收窗口逐漸變小,直至爲0,表示接收端無法再接收新的分組。反之,如果進程能及時讀取數據,甚至速度快於接收的速度,則接收窗口可以不變或增大,直至等於整個接收緩存的大小。

● 窗口的注意點
  1. 雖然發送方的發送窗口是由接收方的接收窗口設置的,但同一時刻不保證它們兩者一樣大。因爲確認分組需要一定時間的滯後才能到達發送方,而該過程中,接收窗口可能發生改變。
  2. 對於不按序到達的數據如何處理並沒有明確規定。最簡單的做法是直接丟棄,但浪費網絡資源。通常是臨時存放在接收窗口中,待缺少的數據到達後,按序交付給應用進程。
  3. 接收方必須有累積確認的功能。接收方可以選擇合適的時候發送確認,或在自己發送分組時捎帶確認信息。但是確認推遲的時間不應過大,TCP標準規定的時間不超過0.5秒。
  4. TCP是全雙工通信,每一方都有發送窗口和接收窗口,分析時務必分清。

2. 超時重傳時間的選擇

● 計算公式

重傳時間的確定是TCP協議最複雜的問題之一
TCP採用自適應算法

  1. 記錄報文發出的時間與收到確認的時間,並由此計算得到報文段往返時間RTT
    TCP會保留RTT的一個加權平均往返時間RTTs(稱平滑往返時間),第一次得到RTT時,RTTs = RTT;此後每次得到一個新的RTT值時,採用下面的公式更新RTTs:
    RTTs(新) = (1 - α) × RTTs(舊) + α × RTT (0 < α < 1)
    若α -> 0,則新舊的RTTs相差不大,也就是受RTT的影響不大,更新的慢;若α -> 1,則新的RTTs受RTT影響大,更新的快。
    RFC-6298推薦的α值爲0.125
  2. RTTD是RTT偏差的加權平均值,與RTTs和新RTT樣本有關。第一次得到RTT時,RTTD取RTT的一半;此後每次得到一個新的RTT值時,採用下面的公式更新RTTD
    RTTD(新) = (1 - β) × RTTD(舊) + β × |RTTs - RTT| (0 < β < 1)
    β的推薦值爲0.25
  3. 超時重傳時間RTO的設置應略大於RTTs,RFC-6298推薦下式計算RTO:
    RTO = RTTs + 4 × RTTD
● Karn算法

在TCP傳輸中,發送方如果重傳了分組,則最後可能會收到多個確認。如何確定分組對應的是哪次重傳就無法得到正確的判斷。而錯誤的判斷,都會導致RTT偏大或偏小,導致導致最後計算的RTTs不準確,影響RTO的有效性。
Karn提出了一個算法:在計算RTTs時,只要是重傳的報文段,就不採用其往返時間樣本。這樣避免了錯誤判斷帶來的誤差。

然而Karn算法帶來了一個新問題:如果由於網絡狀況的變化,報文段的時延突然增加了很多,以至於之後每一次發送的報文段都超時了。此時如果根據Karn算法,RTTs和RTO就永遠無法更新。
因此,Karn算法進行如下修正:報文每重傳一次,就將RTO增大一些;當不再重傳時,才根據上面的公式計算RTTs和RTO
典型的做法是將新的RTO取爲舊RTO的兩倍

修正後的Karn算法能夠區分有效和無效的樣本,改進了往返時間的估測,使得到的RTO更合理且有效。

3. 選擇確認SACK

選擇確認是一種重傳缺少數據的處理辦法。
如果接收方接收到的數據只是未按序到達,缺少一些中間序號的數據,就可以使用選擇確認讓發送方只發送缺少的數據。
假設接收方接收了一些不連續的字節塊並保留在接收窗口中,每兩個不連續的相鄰字節塊都有兩個邊界:左邊界和右邊界每一個數據塊的範圍就是左邊界到右邊界-1。如果給出這些信息,就能讓發送方知道需要重傳的分組和已經正確接收的分組,通知發送方不必重傳已收到的數據。
不連續字節塊的邊界
TCP首部的固定部分沒有字段提供這些邊界信息。要傳送這些信息,必須使用選擇確認SACK字段,這是一個選項字段。
在使用選擇確認字段建立TCP連接時,首部必須加上允許SACK的選項,雙方必須事先商定好。
使用選擇確認字段時,確認號字段的用法不變,只是在首部增加了SACK選項,以報告收到的字節塊的邊界。
SACK選項字段需要1字節指明是SACK選項,以及1字節指明選項佔用的字節數。剩餘用來表示邊界的長度最多隻有38字節。每一個邊界需要4字節,因此最多指明9字節,但指明一個字節塊需要2字節。綜上,SACK最多可以指明4個字節塊的邊界信息。

由於SACK文檔沒有指明如何相應SACK,因此大多數連接仍採用原來的辦法,即重傳所有未確認的數據塊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章