首頁 > 軟體

深入理解TCP協定

2020-06-16 17:54:37

  TCP是面向連線的傳輸層層協定,可以為應用層提供可靠的資料傳輸服務。所謂的面向連線並不是真正意思上的連線,只不過是在傳送資料之前,首先得相互握手,也就是說接收方知道你要發資料給它了。而UDP是面向無連線的傳輸層協定,並不提供可靠的資料傳輸。有一個很恰當的比喻:UDP傳輸就類似於寫信,接收方事先並不知道你要寫信給他;而TCP傳輸就像是打電話,必須等對方按了接聽鍵你才能更他通話。

  那麼TCP又是如何來實現面向連線和可靠性服務的??在討論TCP的可靠資料傳輸之前,我們先看看最簡單的傳輸層服務UDP。

1、UDP

  

  源埠號/目的埠號:同TCP首部中埠號的作用相同

  首部長度:報文段中的位元組數(首部加資料)。

  校驗和:差錯檢測,用於確定當UDP報文段從源到達目地移動時,其中的位元是否發生了變化。

  檢驗和如何計算??

  包括三部分:UDP偽首部、UDP首部、UDP資料部分。偽首部如下所示:

  

  其中,協定欄位:TCP為6,UDP為17,UDP長度即為UDP(包括UDP頭和資料部分)的總長度。

  • 首先把UDP偽首部新增到UDP的前面,然後把UDP首部中的檢驗和欄位填0,把所有的位劃分成16位元的字
  • 把所有16位元的字相加,如果遇到進位,則將高於16位元組的進位部分的值加到最低位上,如:
    • 1011 1011 0101 1110 + 1111 1100 1110 1100 = 1 1011 1000 0100 1010
    • 那麼把1 1011 1000 0100 1011最高位的1加到最低位上得1011 1000 0100 1011
  • 將所有字相加得到的結果為一個16位元的數,將該數取反即為檢驗和欄位 

  從UDP的首部我們就可以看到,UDP是一個很簡陋的傳輸層協定,只負責從傳送端的應用層接收資料,封裝層UDP報文段,然後交給下層傳送到接收端;在接收端,UDP從下層接收資料,然後送達應用層。在該傳輸過程中,UDP之提供一個基本的差錯檢測服務,如果檢測沒有錯誤,就直接交給應用層;否則直接丟棄。

  下面我們來看一下TCP提供的可靠傳輸服務:

2、TCP

  源埠號/目的埠號:用於多路複用/分解來自或送到上層應用的資料。什麼意思呢?處於應用層的進程可能有很多,每個進程都有可能通過傳輸層傳送資料到因特網或者通過傳輸層從因特網中接收資料。那麼當傳輸層從因特網中接收到資料應該傳送給應用層中的哪個進程?或者如何知道從應用層收到的資料是屬於應用層中的哪個服務??其實這些的實現都是通過埠號的標識的。應用層中的每個網路服務都對應著一個埠號,通過埠號來標識對應的服務。所以說埠號是將傳輸層系結到應用層的黏合劑。

  

  序號和確認號:被用來實現可靠資料傳輸服務。

  接收視窗欄位:指示接收方接收緩衝區剩餘大小,用於流量控制。

  首部長度欄位:TCP首部中有一個選項欄位的存在,也就是說TCP首部的長度是可變的,所以需要指明首部的長度。

  選項欄位:用於傳送方與接收方協商最大報文段長度(MSS)時,或在高速網路環境下用作視窗調節因子時使用。還定義了一個時間戳選項。

  RST、SYN、FIN位元:用於連線的建立和拆除。

  PSH位元:當PSH位元被設定時,表明接收方應該立即將資料交給上層。

  URG位元和緊急資料指標:URG位元指示報文段裡存在著被傳送端的上層實體置為“緊急”的資料;緊急資料的最後一個位元組由16bit的緊急資料指標欄位指出。當緊急資料存在並給出緊急資料尾的時候,TCP必須立即通知接收端的上層實體。

  檢驗和欄位:同UDP檢驗和,提供差錯檢測。

  TCP 如何保證資料傳輸的可靠性??

  (1) 在傳送資料之前,進行三次握手,保證與接收端相互可靠通訊。下面來講一個三次握手的過程:

  初始狀態用戶端和伺服器都為CLOSED狀態,伺服器開啟listen監聽客戶連線進入LISTEN狀態;然後用戶端傳送一個SYN包,序列號為j,此時用戶端進入SYN_SENT狀態;當伺服器接收到SYN包時,伺服器進入SYN_RECV狀態,並且傳送一個帶SYN的ACK,確認號為j+1,序列號為k;當用戶端收到這個帶SYN的ACK時,用戶端進入ESTABLISHED狀態,對於用戶端來說,已經確認可以與伺服器通訊了,所以用戶端就可以發資料給伺服器了,此時用戶端發一個ACK(ACK中可以包含資料資訊)到伺服器,確認號為k+1;在伺服器接收到ACK之前,三次握手還沒有完成,雖然用戶端可以發資料給伺服器,但是只能包含在ACK中,而伺服器並不能發資料到用戶端,只有當收到ACK後,伺服器端進入狀態ESTABLISHED狀態。自此,三次握手完成,用戶端可以與伺服器端已經建立了連線,可以互相傳送資料。

  一定要進行三次握手麼,不能只進行兩次或者四次??

  其實這個問題的本質是因特網中通道不可靠, 但是要在這個不可靠的通道上可靠地傳輸資料,三次握手是最小的理論值。

  如果只進行兩次握手,那麼當用戶端傳送一個SYN分組後,會發生兩種情況:

  情況一:伺服器接收到了這個SYN並返回ACK,無論用戶端是否接收到了ACK,伺服器都認為已經與用戶端建立連線了,於是就開始向用戶端傳送資料。但是如果客戶段沒有收到ACK,那麼用戶端會認為與伺服器沒有建立連線,就不會接收伺服器發來的資料,也就是說直接丟棄伺服器發來的資料,伺服器發出的訊息超時了,就重複傳送資料,這就產生了死鎖。

  情況二:用戶端發出的第一個連線請求報文段並沒有丟失,而是在某個網路結點長時間的滯留了,以致延誤到連線釋放以後的某個時間才到達伺服器。本來這是一個早已失效的報文段。但伺服器收到此失效的連線請求報文段後,就誤認為是用戶端再次發出的一個新的連線請求。於是就向用戶端傳送ACK,但是此時用戶端沒有發出請求,所以並不會理睬這個ACK,而伺服器又開始發資料給用戶端了,這時候,用戶端又把這些資料都丟棄了,而伺服器發出的訊息超時了,就重複傳送資料,也產生了死鎖。

 

  (2) 通過確認和重傳機制來保證資料的完整性和按序交付

  TCP把資料看成是無結構和有序的位元組流,所以上面所說的報文段的序列號是該報文段首位元組的位元組流編號,而報文段中的確認號是主機期望從用戶端收到的下一個位元組的序號。我們來舉個例子:

  假設TCP從應用層接收到3000個位元組長度的資料,而TCP最大報文長度MSS為1460,那麼就要對資料進行分段,第一段資料為0~1459位元組,第二段為1460~2919位元組,第三段為2920~2999位元組,那麼這三個報文段的序列號分別為0、1460、2920。

  假如伺服器端接收到用戶端發過來的第一個報文段0~1459位元組,那麼它期望收到的下一個位元組的序列號為1460,那麼在返回給用戶端的ACK中確認號即為1460,然後伺服器又收到用戶端發來的2920~2999位元組的報文,但是未收到1460~2919位元組,那麼伺服器端繼續期望下一個接收位元組為1460,所以返回的ACK中的確認號依舊為1460。TCP只確認直到第一個未收到位元組之前的位元組,所以TCP提供的是累積確認接收方保留失序的位元組,同時等待缺少的位元組來填補間隔。

  當然在如此錯綜複雜的網路中,即使三次握手建立連線了,也不可能每次傳送資料都能成功到達目的地。用戶端每次向網路中傳送一個報文號,其實還會繼續快取該報文,指導收到伺服器端發過來的ACK確認伺服器收到了該報文,然後才會丟棄。但是當傳送的報文段在網路中發生丟包了或者產生了位元出錯又或者伺服器返回的ACK丟失了,那麼用戶端將都收不到ACK。那麼怎麼辦?總不能一直等著吧?

  用戶端通過一個定時器超時機制來保證用戶端不會無限制地等待。也就是當傳送一個報文段後,就啟動定時器,當發生超時了還未收到伺服器發來的ACK時,用戶端就重新傳送該報文段。但是設定多長時間呢??從用戶端傳送一個報文到接收到ACK相當於一個來回,我們用往返時間RTT來表示,設定的這個定時器時間至少得大於RTT吧。如果是ACK丟失了,那麼伺服器如果收到了這個重發的報文,那麼資料不就重複了麼??伺服器通過序列號來保證資料的無冗餘,當伺服器收到了這個重複的封包時,便知道用戶端沒有收到ACK超時了,就直接把它丟棄,然後返回給用戶端一個最新的ACK。

  (3) TCP提供了流量控制和擁塞控制

  流量控制其實是一個速度匹配服務,也就是說傳送方傳送資料的速率要與接收方應用程式讀取速率相匹配,以消除接收端緩衝區溢位的可能性。在TCP首部中有一個欄位叫做接收視窗欄位,它就是用來通知傳送端伺服器上剩餘的緩衝區的大小(rwnd)的。

  TCP提供的擁塞控制並不是網路輔助的擁塞控制,而是端到端的擁塞控制,因為IP層並不向端系統提供顯式的網路擁塞反饋。那麼TCP傳送方如何限制它的傳送速率?傳送方又如何知道路徑上是否擁塞?

  上面提到當封包在網路中丟失時就可能發生超時,而伺服器段可能收到冗餘的封包,當然用戶端也不例外,也可能收到冗餘的ACK。所以我們把丟包事件定義為:要麼出現超時,要麼收到來自接收端的3個冗餘的ACK。當丟包事件發生了,用戶端就知道鏈路上存在擁塞。

  傳送端維護著一個擁塞視窗(cwnd),一個傳送方的緩衝區中未被求確認的資料量不會超過cwnd和rwnd(流量控制中接收視窗欄位,伺服器上剩餘的緩衝區的大小)的最小值。這個約束限制了傳送方未被確認的資料量,也就間接限制了傳送速率。

  其實TCP是按照如下原則來設定傳送速率:

  • 一個丟失的報文段意味著擁塞,因此當丟失報文段時應降低TCP傳送方的速率。
  • 一個確認報文段指示該網路正在向接收方交付傳送方的報文段,所以,當對先前未確認報文段的確認到達時,能夠增加傳送方的速率。
  • 因為IP層並不向上層提供顯式的網路擁塞反饋,所以TCP是通過ACK和丟包事件來充當隱式信號進行頻寬探測。

  那麼問題又來了cwnd的值又該如何設定呢??

  通過TCP擁塞控制演算法慢啟動擁塞避免快速恢復

關於擁塞控制演算法具體實現過程說來話長

更多詳情見請繼續閱讀下一頁的精彩內容http://www.linuxidc.com/Linux/2015-08/122157p2.htm


IT145.com E-mail:sddin#qq.com