歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> 深入理解TCP協議

深入理解TCP協議

日期:2017/2/28 13:58:36   编辑:Linux教程

  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擁塞控制算法慢啟動擁塞避免快速恢復

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

Copyright © Linux教程網 All Rights Reserved