深刻理解TCP(一)

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

  那麼TCP又是如何來實現面向鏈接和可靠性服務的??在討論TCP的可靠數據傳輸以前,咱們先看看最簡單的傳輸層服務UDP。緩存

一、UDP服務器

  

  源端口號/目的端口號:同TCP首部中端口號的做用相同網絡

  首部長度:報文段中的字節數(首部加數據)。spa

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

  檢驗和如何計算??指針

  包括三部分:UDP僞首部、UDP首部、UDP數據部分。僞首部以下所示:blog

  

  其中,協議字段: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提供的可靠傳輸服務:

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

關於擁塞控制算法具體實現過程說來話長,下一篇博文持續更新,敬請關注博主。

版權全部,歡迎轉載,轉載請註明出處。

相關文章
相關標籤/搜索