網絡通信協議之-定製篇

TCP如何保證消息順序以及可靠性到達

TCP如何保證消息順序以及可靠性到達,以及TCP的流量控制,擁塞控制
TCP可靠性傳輸傳輸的工作原理:

  1. 停止等待協議
  2. 連續ARQ協議

TCP可靠性傳輸傳輸的實現

  1. 以字節爲單位的滑動窗口
  2. 超時重傳時間的選擇
  3. 選擇確定SACK

面向連接:意味着兩個使用TCP的應用(通常是一個客戶和一個服務器)在彼此交換數據之前必須先建立一個TCP連接。在一個TCP連接中,僅有兩方進行彼此通信。廣播和多播不能用於TCP。
TCP通過下列方式來提供可靠性:

  1. 應用數據被分割成TCP認爲最適合發送的數據塊。這和UDP完全不同,應用程序產生的數據報長度將保持不變。 (將數據截斷爲合理的長度)
  2. 當TCP發出一個段後,它啓動一個定時器,等待目的端確認收到這個報文段。如果不能及時收到一個確認,將重發這個報文段。 (超時重發)
  3. 當TCP收到發自TCP連接另一端的數據,它將發送一個確認。這個確認不是立即發送,通常將推遲幾分之一秒 。 (對於收到的請求,給出確認響應) (之所以推遲,可能是要對包做完整校驗)
  4. TCP將保持它首部和數據的檢驗和。這是一個端到端的檢驗和,目的是檢測數據在傳輸過程中的任何變化。如果收到段的檢驗和有差錯,TCP將丟棄這個報文段和不確認收到此報文段。 (校驗出包有錯,丟棄報文段,不給出響應,TCP發送數據端,超時時會重發數據)
  5. 既然TCP報文段作爲IP數據報來傳輸,而IP數據報的到達可能會失序,因此TCP報文段的到達也可能會失序。如果必要,TCP將對收到的數據進行重新排序,將收到的數據以正確的順序交給應用層。 (對失序數據進行重新排序,然後才交給應用層)
  6. 既然IP數據報會發生重複,TCP的接收端必須丟棄重複的數據。(對於重複數據,能夠丟棄重複數據)
  7. TCP還能提供流量控制。TCP連接的每一方都有固定大小的緩衝空間。TCP的接收端只允許另一端發送接收端緩衝區所能接納的數據。這將防止較快主機致使較慢主機的緩衝區溢出。(TCP可以進行流量控制,防止較快主機致使較慢主機的緩衝區溢出)TCP使用的流量控制協議是可變大小的滑動窗口協議。

字節流服務::兩個應用程序通過TCP連接交換8bit字節構成的字節流。TCP不在字節流中插入記錄標識符。我們將這稱爲字節流服務(bytestreamservice)。
TCP對字節流的內容不作任何解釋:: TCP對字節流的內容不作任何解釋。TCP不知道傳輸的數據字節流是二進制數據,還是ASCII字符、EBCDIC字符或者其他類型數據。對字節流的解釋由TCP連接雙方的應用層解釋。
TCP保證消息順序:
大家都知道,TCP提供了最可靠的數據傳輸,它給發送的每個數據包做順序化(這看起來非常煩瑣),然而,如果TCP沒有這樣煩瑣的操作,那麼,可能會造成更多的麻煩。如造成數據包的重傳、順序的顛倒甚至造成數據包的丟失。
那麼,TCP具體是通過怎樣的方式來保證數據的順序化傳輸呢?

主機每次發送數據時,TCP就給每個數據包分配一個序列號並且在一個特定的時間內等待接收主機對分配的這個序列號進行確認,如果發送主機在一個特定時間內沒有收到接收主機的確認,則發送主機會重傳此數據包。接收主機利用序列號對接收的數據進行確認,以便檢測對方發送的數據是否有丟失或者亂序等,接收主機一旦收到已經順序化的數據,它就將這些數據按正確的順序重組成數據流並傳遞到高層進行處理。

具體步驟如下:

  1. 爲了保證數據包的可靠傳遞,發送方必須把已發送的數據包保留在緩衝區;
  2. 併爲每個已發送的數據包啓動一個超時定時器;
  3. 如在定時器超時之前收到了對方發來的應答信息(可能是對本包的應答,也可以是對本包後續包的應答),則釋放該數據包占用的緩衝區;
  4. 否則,重傳該數據包,直到收到應答或重傳次數超過規定的最大次數爲止。
  5. 接收方收到數據包後,先進行CRC校驗,如果正確則把數據交給上層協議,然後給發送方發送一個累計應答包,表明該數據已收到,如果接收方正好也有數據要發給發送方,應答包也可方在數據包中捎帶過去。

注意

一、儘管TCP能夠有效的保證每個字節序和包順序是按照順序接收的,但是仍然會存在粘包的問題,具體如下:

1. 粘包產生的原因
如果客戶端連續不斷的向服務端發送數據包時,服務端接收的數據會出現兩個數據包粘在一起的情況,這就是TCP協議中經常會遇到的粘包以及拆包的問題。
**傳輸層的UDP協議是否會發生粘包或者拆包問題?** 不會。
UDP是基於報文發送的,在UDP首部採用了16bit來指示UDP數據報文的長度,因此在應用層能很好的將不同的數據報文區分開,從而避免粘包和拆包的問題。
**傳輸層的TCP協議是否會發生粘包或者拆包問題?** 會。
原因有以下兩點:
1. TCP是基於字節流的,雖然應用層和傳輸層之間的數據交互是大小不等的數據塊,但是TCP把這些數據塊僅僅看成一連串無結構的字節流,沒有邊界;
2. 在TCP的首部沒有表示數據長度的字段,基於上面兩點,在使用TCP傳輸數據時,纔有粘包或者拆包現象發生的可能。  
2、粘包/拆包的表現形式

現在假設客戶端向服務端連續發送了兩個數據包,用packet1和packet2來表示,那麼服務端收到的數據可以分爲三種,現列舉如下:

  • 第一種情況,接收端正常收到兩個數據包,即沒有發生拆包和粘包的現象,此種情況不在本文的討論範圍內。

  • 第二種情況,接收端只收到一個數據包,由於TCP是不會出現丟包的,所以這一個數據包中包含了發送端發送的兩個數據包的信息,這種現象即爲粘包。這種情況由於接收端不知道這兩個數據包的界限,所以對於接收端來說很難處理。

  • 第三種情況,這種情況有兩種表現形式,如下圖。接收端收到了兩個數據包,但是這兩個數據包要麼是不完整的,要麼就是多出來一塊,這種情況即發生了拆包和粘包。這兩種情況如果不加特殊處理,對於接收端同樣是不好處理的。

3、粘包/拆包發生的原因

發生TCP粘包或拆包有很多原因,現列出常見的幾點:
1、要發送的數據大於TCP發送緩衝區剩餘空間大小,將會發生拆包。
2、待發送數據大於MSS(最大報文長度),TCP在傳輸前將進行拆包。
3、要發送的數據小於TCP發送緩衝區的大小,TCP將多次寫入緩衝區的數據一次發送出去,將會發生粘包。
4、接收數據端的應用層沒有及時讀取接收緩衝區中的數據,將發生粘包。

4、粘包/拆包的解決辦法

通過以上分析,我們清楚了粘包或拆包發生的原因,那麼如何解決這個問題呢?解決問題的關鍵在於如何給每個數據包添加邊界信息,常用的方法有如下幾個:

  1. 發送端給每個數據包添加包首部,首部中應該至少包含數據包的長度,這樣接收端在接收到數據後,通過讀取包首部的長度字段,便知道每一個數據包的實際長度了。
  2. 發送端將每個數據包封裝爲固定長度(不夠的可以通過補0填充),這樣接收端每次從接收緩衝區中讀取固定長度的數據就自然而然的把每個數據包拆分開來。
  3. 可以在數據包之間設置邊界,如添加特殊符號,這樣,接收端通過這個邊界就可以將不同的數據包拆分開。

如何選擇合適的Header來避免在數據包中也出現同樣的Header內容呢?如下所示:

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