1. 程式人生 > 其它 >TCP粘包和拆包

TCP粘包和拆包

學習起因

  本來學過計算機網路的,但是印象中好像又沒學過(可能是因為不用考),所以面試問到還是一臉懵...........

TCP粘包

定義

  TCP在傳輸位元組流時,傳送方傳送的若干包資料到達接收方時粘成了一個包,從接受方來看就是前一包資料的尾緊接著後一包資料的頭。起因存在於傳送方也存在於接收方。

起因

  傳送方造成:TCP預設使用Nagle演算法(主要作用:減少網路中報文段的數量),具體做兩件事:1)只有上一個分組得到確認,才會傳送下一個分組(如果報文長度超過1460就直接發出,不等待確認)。2)收集多個小分組,在一個確認到來時一起傳送。相當於如傳送報文的大小小於傳送緩衝區大小,傳送緩衝區將多次寫入的資料一次傳送出去。

  接收方造成:接受資料的應用層沒有及時將接受緩衝區的內容讀取(TCP將接收的資料存入緩衝區中,由應用程式去緩衝區獲取資料,而不是直接傳送給應用程序)。

解決方案

  傳送方:關閉TCP使用Nagle演算法,使用TCP_NODELAY選項關閉演算法。而接收方沒有辦法處理粘包現象,只能將問題交由應用層處理。

  應用層:在應用層層面解決問題的話,不僅解決接收方粘包問題,還能解決傳送方粘包問題。解決方案:迴圈解決,讀完一條資料,再讀取下一條資料,直到所有資料都被處理完成。如何將每條資料分割開來:1)格式化資料:每條資料都由固定的資料格式(開始符和結束符),但是要保證資料裡面沒有開始符和結束符。2)傳送長度:每條資料前用4個位元組來表示資料的長度,應用層可根據這個長度來判斷開始位置和結束位置。

TCP拆包

定義

  如果資料包長度太大,超過MSS(最大TCP報文長度,不包括TCP和IP的首部)的長度,TCP就會將報文分開傳輸,這樣就會造成一個完整的資料報文在傳輸過程中被分成了幾部分,這就是拆包。

起因

  傳送方:1)應用層程式寫入資料時的資料大於套接字緩衝區的的大小。2)TCP報文長度-TCP首部長度大於MSS時,將發生拆包。

解決方案

  應用層:在應用層層面解決問題的話,不僅解決接收方粘包問題,還能解決傳送方粘包問題。解決方案:迴圈解決,讀完一條資料,再讀取下一條資料,直到所有資料都被處理完成。如何將每條資料分割開來:1)格式化資料:每條資料都由固定的資料格式(開始符和結束符),但是要保證資料裡面沒有開始符和結束符。2)傳送長度:每條資料前用4個位元組來表示資料的長度,應用層可根據這個長度來判斷開始位置和結束位置。

注意

  粘包和拆包只發生於使用TCP傳輸方式,不存在於UDP傳輸方式,因為TCP是面向流傳輸的,而UDP是面向訊息傳輸的,這就決定了TCP在傳輸過程中將上層傳輸的報文視作流資料,每個流資料可能包含上層的幾個報文,而TCP報文首部又沒有區分每個報文的標記,(比如起始標記和結束標記,包長度等),這就決定了TCP可能會產生粘包和拆包的現象。UDP每次傳輸都是一個訊息一個訊息地傳輸,而接收方每次也只接收一個訊息,而UDP首部也是有著包長度來作為每個訊息包的長度,以此對每個訊息包進行區分。

UDP首部

  首部8個位元組,源埠和目標埠各佔2個位元組,UDP報文長度佔2個位元組,檢驗和佔2個位元組。此處引用《圖解TCP/IP》的UDP報文首部組成圖。

包長度可看出每個UDP資料包的最大長度可為2^16-1位元組,所以UDP最大可傳輸的資料長度為2^16-1-8(UDP首部長度)-20(IP首部長度)

檢驗和的作用是判斷資料是否發生錯誤,如果發生錯誤就將包丟掉,但不提供差錯恢復的能力。

TCP首部

源埠和目標埠:各佔兩個位元組,分別寫入源埠和目標埠。

序號:佔4位元組,範圍位[0-2^32-1],共2^32個序號,序號增加到2^32-1後,下一個序號就又回到0了。TCP面向位元組流傳輸,每一個TPC連線傳輸的位元組流中的每一個位元組都按順序編號。整個要傳送的位元組流的起始位置在建立連線時就設定了、首部中的序號代表的就說傳送位元組流的第一個位元組的序號,例如,一個位元組的序號為301,而傳送的資料長度為100,即最後一個位元組序號為400,所以如果有下一個報文段,那麼資料序號應該從401開始。
確認號:佔4個位元組,期望收到對方下一個報文段的第一個資料位元組的序號。例如,B傳送給A的最後一個位元組的序號為400,那麼A就希望B下一次傳送的資料的第一個位元組的序號為401。所以ack就為401。

資料偏移:佔4位,表示資料起始段到TCP報文起始處的長度,也就是TCP首部長度。因為首部中還有不確定長度的選項欄位,所以資料偏移很有必要。而且只有4位說明能表示的最大長度為15,但不是15位元組,因為資料偏移的單位是4位元組,所以4*15為60,首部最多為60位元組,所以選項長度不能超過40位元組。

保留:佔6位,保留為今後使用。

接下來是6個標誌位。

緊急URG:當URG欄位為1時,表明緊急指標欄位有效。用來告訴系統此報文有緊急資料,需要優先發送(相當於高優先順序的資料),而不要按照原來的排隊順序傳送。當URG設定為1時,傳送方應用程序就把緊急資料插入到本報文資料的最前面,而在緊急資料後面的還是普通資料,如果URG不設定為1,緊急資料就會按順序排在普通資料後,沒有插入的操作。

ACK:僅當ACK=1時確認號欄位才有效。TCP規定建立連線後所有傳送的報文的ACK都必須為1。

推送PSH:當兩個程序相互通訊時,如果一端的應用程序希望在鍵入一個命令後立即能收到對方的回覆。傳送方將PSH置為1,並立即建立一個報文段傳送出去,接收方在收到PSH=1的報文段後,會盡快交付上層應用,而不會等緩衝區填滿再交付。

復位RST:當RST=1時,表明TCP連線中發生了嚴重錯誤,必須釋放連線,然後重新建立傳輸連線,RST置為1還用來拒絕一個非法的報文段,或拒絕開啟一個連線。

同步SYN:在建立連線時用來同步序號。當SYN為1,ACK為為0時,表明這是一個連線請求報文段,對方若同意建立連線就傳送一個SYN=1,ACK=1的接受連線報文。

終止FIN:用來釋放一個連線。當FIN=1時,表明此報文段的傳送資料已傳送完畢,並要求釋放傳輸連線。

視窗:佔2個位元組,用來告訴傳送方自己能接收多大的資料,比如此時確認序號為701,視窗值為1000,那麼就相當於告訴傳送方,下一個傳送的資料第一個位元組的序號應該是701,只能接收到1700。所以視窗值是可以動態變化的。

檢驗和:佔2個位元組,檢驗和欄位檢驗範圍包括首部和資料兩部分。

緊急指標:佔2個位元組,緊急指標僅在URG=1時才有意義,它指出本報文段中緊急資料的位元組數(緊急資料後就是普通資料)。指出緊急資料的末尾在報文段中的位置,即使視窗為0時也可以傳送緊急資料。

TCP Option

每個選項的開始都是1個Byte的kind欄位,說明選項的型別,Kind為0/1的時候,選項只佔1個Byte,其他選項在kind欄位後面還有len位元組,說明總長度包括kind和len的位元組。看下圖

從圖中我們可以看到

0. 代表選項表結束

1.代表無操作

2.代表MSS

3.代表視窗擴大銀子

8.代表時間戳

其他的kind值為4/5/6/7 四個選項被稱為選擇ACK及回顯選項,目前回顯選項已經被時間戳給代替!

引用https://blog.csdn.net/wdscq1234/article/details/52423272