網絡編程-TCP socket 拆包 粘包

說明

打了刪除線的,都是有問題的錯誤的描述。html

背景

對於這類問題,發現仍是最終要回歸到經典書籍,即牛逼的unix網絡編程 這樣的書,否則看再看的文章和網絡資料,都不必定能看得懂,由於網絡資料和網友的理解不必定很全面,他們最原始的最源頭的參考資料,也就是這麼幾本書。無非是,他們肯比較吃苦,把書多看了幾遍,能用本身的語言表達出來,固然,能夠確定的是,只要是複述別人的話,確定是有很多的漏洞的。後面的人,須要理解清楚,就慘了。最終,仍是得,把經典書籍給撿起來,和他們同樣,得多看幾遍。java

tcp協議的幾張架構圖和流程圖

最關鍵的地方就是這裏了。要深刻的真正的理解爲何會發生拆包和粘包的狀況。直接看unix網絡編程這一本書就夠了。程序員


協議分層 編程


握手:須要三個分節 數組

握到一半的時候,叫半鏈接。這個時候,還不能進行數據通訊。只有等徹底創建鏈接,即狀態變爲established的時候,才能夠讀寫數據。服務器


關閉:須要四個分節,由於確認分節和響應分節是分開的。具體緣由是,由於響應分節超過1s,就先發確認分節。 網絡

關到一半的時候,叫半關閉。這個時候,仍然能夠進行通訊。由於有可能,路由在路上的數據尚未到達目的地。數據結構


tcp socket 11種狀態-狀態轉換圖 架構


握手鍊接、讀寫數據、關閉的完整流程 app


tcp socket在內核裏的緩衝區內存


操做分節和確認分節
無論是什麼操做,好比握手操做或者關閉操做,都有一個專門的分節來表示它們。而每一個操做分節都須要確認,因此也就有了確認分節,確認分節的內容是ACK操做分節的序號+1。例如,ACK M+1。操做分節的序號,指的是,當前數據的分節序號,就是一個編號而已。

一些概念

數據按是否完整,分爲兩塊(或者說,有兩種叫法)
1.數據報文
要發送的完整數據
2.數據包
數據子包 分節 數據報,反正,無論什麼叫法,就是隻是完整數據的一部分。

並且,分節,在每一層都有可能往下拆分爲多個子分節。也就是說,只有應用層的數據纔是完整的數據報文,其餘的層,包括傳輸層tcp 網絡層ip 數據鏈路層,都是子分節。


另外,不一樣層,對分節的叫法,也不同,但其實,本質上都是分節,即表明的是不完整的子數據:
1.tcp
分節
2.ip
數據報datagram
3.數據鏈路層
幀frame

爲何會發生拆包和粘包?

1.拆分
是由於數據太大

2.合併
是由於數據過小


具體細節
tcp socket 內核緩衝區:包含發送緩衝區和接受緩衝區,客戶端和服務器都有兩個緩衝區
1.何時發生拆包?
數據比緩衝區大。就會被拆成多個獨立的分節。
2.何時發生粘包?
數據比緩衝區小,就有可能把多個數據拼接爲一個分節。

可是,網絡協議的目的,整體上來講,是要避免減小拆包和粘包的狀況,由於拆包和粘包以後,在路由到目的地的時候順序不一致,接受度要從新還原順序。這是浪費。

並且,在網絡協議的每一層,都有可能發生拆包和粘包。

數據包的路由

中間經歷不少路由器

錯誤的理解

TCP協議層已經解決粘包 拆包問題,爲何還要應用層去解決呢 TCP協議層標記了數據包的順序,可是接受方收到後如何排序?數據不是以流的形式存在嗎?總數據長度固定,這個能夠區別不一樣的請求數據。可是接收方怎麼解決同一次請求的不一樣數據包排序的問題?

數據包的順序不須要應用層解決,由於tcp協議層和操做系統內核已解決。


本質是要識別某一次數據的總長度和前後順序
1.總長度肯定
2.前後順序編號 //這個是TCP網絡協議層已經解決的,不須要應用層解決

全部的解決方法都是圍繞以上兩個問題,而後解決以上兩個問題。


總結
上面的理解都是有問題的,錯誤的。

首先,關於順序編號,有兩種1.每一個數據包(即分節)都有編號 2.分節裏的每一個字節,都有編號。不過,無論是哪種編號,目的都是爲了在接收端從新排序還原數據原始順序。

其次,沒有什麼總長度,只有每一個數據包的長度。固然,每一層的數據包都有可能被拆分紅多個分節(即子包),因此,每一個數據包的長度只是知道它本身(即當前數據包)的長度,而不知道要發送的整個數據的總長度。因此,這個時候,才須要咱們應用層去解決拆包和粘包的問題,說白了,就是要找到兩次數據的邊界在哪裏,好比,我聊天的時候發送了兩次數據(兩條消息),所謂的拆包和粘包,就是要解決如何區別這兩個數據的問題。固然,短數據,通常就一個數據包。

最佳實踐

應用層才須要解決拆包 粘包的問題,不過,咱們具體開發程序的時候,通常使用netty這樣的通訊框架,也就是說,netty這樣的通訊框架,已經幫助咱們處理了拆包 粘包的問題。netty這樣的通訊框架,處理的就是傳輸層tcp socket的拆包 粘包問題。

具體如何處理?
netty有不一樣的解決方案類,這些解決方案類,每一個都是對應一個解決方案。具體的解決方案,就是:
1.分隔符標誌
使用分隔符,區分數據
2.總長度
使用總長度,區分數據。注意,這裏的總長度,是應用層協議的數據的總長度,而不是傳輸層的總長度,服務器端和客戶端兩邊就經過應用層協議的報文的數據結構的總長度字段,來區分數據。

粘包和拆包的解決方法有什麼不一樣嗎?

這他媽都是問題啊,一堆問題,說明你根本沒有理解清楚。只是知道一點理論。這不是學習的好方法啊。


無論是拆包,仍是粘包,本質上要解決的問題是,區分兩次發送的數據。具體的辦法是,
1.要麼使用分隔符區分
2.要麼使用總長度

使用分隔符,好比,http,使用換行符。

總長度,好比,netty。

通常的應用層協議都是使用總長度。

http協議

請求行
頭部字段
內容

以上三項是經過換行符分離。若是包含了換行符,那麼轉義字符。

netty是如何解決粘包 拆包

以前有總結過。解決方案是不一樣的解決方案有不一樣的類。

細節。參考那篇筆記。

原理確定是和前面提到的原理同樣。

time_wait狀態

客戶端主動關閉時的最後一個狀態

TCP狀態有11種

客戶端和服務器,都有11種狀態。


狀態轉換圖


總共好幾張圖,很重要,很經典

接受端-內核緩衝區:已使用和未使用

接受端-內核緩衝區,包含了已使用和未使用這些數據,而且會把這些數據發送給發送端。而這就是所謂的窗口大小,知道了接受端的剩餘大小,發送端就能夠控制發送速度和發送數據的流量大小。

規範RFC

網絡和協議這一塊,全部的官方標準和規範,就是RFC文檔。

UDP

User datagram protocol用戶數據報文協議。

和TCP最大的區別就是,不可靠。

爲何要分層?

爲什麼要分層?分層的本質?每一層的本質?
每一層解決一個問題,不分層也能夠,可是,這樣的話,就是一個層解決全部問題。

這就MVC框架同樣,每一層解決不一樣的問題。

好比,IP網絡層,包含了源IP和目的IP,主要做用是找到對方機器,把數據路由過去。

而,TCP傳輸層,是確保數據傳輸過程中的可靠性。具體來講就是1.接受者必須收到數據2.數據不能丟失,不然服務器重發3.數據不能重複,不然客戶端去重。

爲何鏈接是三次,關閉是四次?

由於鏈接的時候,服務器的確認和同步一塊兒發送。
而,關閉,服務器是分開發送。即先發送確認,再發送關閉。

爲何鏈接和關閉,服務器端,一個能夠一塊兒發送,一個只能分開?這個是根據響應時間來肯定的。即,若是響應(無論是鏈接-同步Syn,仍是關閉-FIN,仍是讀寫數據-響應,這三種狀況其實本質上都是響應)的時間是毫秒級別200ms,那麼一塊兒發送,若是是秒級別1s,那麼分兩次發送,先發送確認,後發送響應。

鏈接的syn分節,是不帶數據的,只包含頭部字段。

誰先關閉?

通常都是客戶端主動關閉。早期http1.0是短鏈接,因此是服務器主動關閉,後期1.1是長鏈接,客戶端先關閉。

TCP的底層知識

TCP協議是可靠的協議,主要確保幾點:
1.數據有序
編號。
每一個分節編號(好比,12345)。
每一個字節編號(好比,第一個分節1~1000,第二個分節1000~2000)。
編號的目的是,由發送端編號,接受端收到數據以後,若是亂序,那麼從新還原順序。
2.數據不能丟失
丟失是指接受端沒有收到,發送端要重發。具體實現原理是?接受端是否接受到數據,須要發送確認給發送端。
3.數據不能重複
接受端,若是收到重複分節數據,那麼拋棄重複數據,由於數據分節有編號。

應用層協議

正由於非應用層的協議,確保了數據有序,如今剩下來的惟一要解決的問題,就是怎麼區分不一樣的數據。

由於數據是流,TCP協議的socket是基於字節流來通訊的。

怎麼區分?
首先要確認一點的是,必須由應用層協議來解決。
其次,就是具體的解決方法的問題,通常有兩種方法:
1.分隔符
2.應用層協議的數據報文的數據結構,有一個數據報文的總長度字段

---附加---

什麼是粘包 拆包

2個數據包變成一個包 是粘包
2個包變成了3個包 是拆包


解釋
首先,要明白兩個概念
1.報文/消息/文件
當前這一次發送的完整內容
2.數據包
一個大的報文1若是一次發送不完 會被拆分爲多個小的數據包1數據包2

其次,還要知道tcp協議裏包含了緩衝區這個東西 發送數據的流程是:
用戶進程——操做系統內核的緩衝區——網絡——目標機器

在用戶進程——內核的緩衝區
這一步(這一步是完整報文),數據的流程是這樣的,
1.先把數據複製到緩衝區
2.緩衝區的數據何時到網絡 看狀況。因此在緩衝區——網絡這一步(這一步是數據包-完整報文的一部分),每一個數據包的內容會出現如下幾種狀況:
1.報文1
2.報文2
3.報文1的部分數據 + 報文2 //3 4是拆包,就是有一個報文的數據被拆分紅了兩部分
4.報文2的部分數據 + 報文1
5.報文1 + 報文2 //5是粘包 就是兩個報文在一個數據包裏


爲何會出現粘包和拆包的狀況?
發生TCP粘包、拆包主要是因爲下面一些緣由:

應用程序寫入的數據大於套接字緩衝區大小,這將會發生拆包
應用程序寫入數據小於套接字緩衝區大小,網卡將應用屢次寫入的數據發送到網絡上,這將會發生粘包
進行MSS(最大報文長度)大小的TCP分段,當TCP報文長度-TCP頭部長度>MSS的時候將發生拆包。
接收方法不及時讀取套接字緩衝區數據,這將發生粘包。
……

my.oschina.net/andylucc/bl…

總結
關鍵在一點
1.緩衝區的數據何時到網絡 不肯定 要看狀況

另外
2.緩衝區的大小很大程度上決定了何時緩衝區數據到網絡

底層原理

之因此會發生所謂粘包 拆包的緣由是由於tcp協議這一層的數據是字節流,接受數據方不知道數據何時該結束。


粘包與拆包是因爲TCP協議是字節流協議,沒有記錄邊界所致使的。 因此如何肯定一個完整的業務包就由應用層來處理了。 (這就是分包機制,本質上就是要在應用層維護消息與消息的邊界。) 分包機制通常有兩個通用的解決方法:
1,特殊字符控制,例如FTP協議。
2,在包頭首都添加數據包的長度,例如HTTP協議。

做者:祖春雷 連接:www.zhihu.com/question/37… 來源:知乎 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。


爲何是應用層解決拆包 粘包?tcp層協議不是有報文長度字段嗎?

解決方法

只能在上層/應用層協議(即應用層)解決這個問題。幾種方法

1.換行符
2.頭部和數據部分
頭部的長度字段規定報文長度
3.固定長度


netty怎麼解決?
以上三種方法 netty都有對應的類。

換行符-須要處理包含換行符的狀況

轉義字符? 轉義爲何能解決問題? java轉義字符?

報文究竟是什麼

要發送的完整內容


報文和數據包的區別
1.報文是完整的信息 即你要發送的內容 好比 聊天文字 文件 http請求消息/響應消息
2.若是是短消息 好比聊天文字 一個數據包就夠了 這個時候數據包就是報文 基於udp報文協議
若是是大數據 好比文件 一個數據包不夠 就多個數據包 這個時候一個完整報文分爲多個數據包 基於tcp數據包協議
blog.51cto.com/91xueit/135…


不一樣協議層的叫法可能不同
總結一下。frame對應mac,packet對應ip,datagram對應udp,segment對應tcp,message對應app。但願對你們有用。
blog.csdn.net/wang7dao/ar…


http協議的報文
1.請求
請求行
請求頭部字段 //包含請求體的長度字段
內容

2.響應
同上

字節序號

字節流的字節可能亂序 接受方經過字節序號還原發送順序。


爲何會亂序
由於兩個電腦之間傳輸數據 中間通過不少的路由器路由轉發 不一樣的報文走的多是不一樣的路徑 到達目標ip的時間前後順序可能不同


是字節序號仍是數據包序號
都有編號。
無論是哪種序號 編號的目的都是爲了解決接收方還原數據順序的目的。


接收方具體是如何實現還原的?

通告/窗口大小

就是tcp緩衝區剩餘內存大小

tcp緩衝區是什麼?

分類
有發送緩衝區和接收方緩衝區


是什麼
兩個套接字 各自都有寫和讀緩衝區。

套接字的緩衝區是基於tcp協議的緩衝區,兩者一一對應

套接字編程時 有發送緩衝區和接受緩衝區字段能夠設置值的大小

c.biancheng.net/cpp/html/30…

juejin.im/post/5b344a…

咱們平時用到的套接字其實只是一個引用(一個對象ID),這個套接字對象其實是放在操做系統內核中。這個套接字對象內部有兩個重要的緩衝結構,一個是讀緩衝(read buffer),一個是寫緩衝(write buffer),它們都是有限大小的數組結構。

當咱們對客戶端的socket寫入字節數組時(序列化後的請求消息對象req),是將字節數組拷貝到內核區套接字對象的write buffer中,內核網絡模塊會有單獨的線程負責不停地將write buffer的數據拷貝到網卡硬件,網卡硬件再將數據送到網線,通過一些列路由器交換機,最終送達服務器的網卡硬件中。

一樣,服務器內核的網絡模塊也會有單獨的線程不停地將收到的數據拷貝到套接字的read buffer中等待用戶層來讀取。最終服務器的用戶進程經過socket引用的read方法將read buffer中的數據拷貝到用戶程序內存中進行反序列化成請求對象進行處理。而後服務器將處理後的響應對象走一個相反的流程發送給客戶端,這裏就再也不具體描述。

做者:老錢 連接:juejin.im/post/5b344a… 來源:掘金 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

注:核心流程:用戶態——內核態:緩衝區。 所謂的套接字/tcp 緩衝區就是內核態的緩衝區,這個緩衝區在內存,全部緩衝區都是在內存。


用戶進程的緩衝區和操做系統內核tcp/套接字的緩衝區?


udp有沒有緩衝區?
沒有


做用
1.工做流程
數據先到操做系統內核緩衝區,內核有專門線程處理緩衝區的數據用於讀寫。
2.具體細節
緩衝區滿了 一次性讀寫?不是。看狀況。具體見下文。
3.緩衝區本質的做用 或者說好處


緩衝區與窗口大小
兩者同樣


緩衝區的數據什麼時候被處理 即被髮送到網絡?
TCP協議獨立於 write()/send() 函數,數據有可能剛被寫入緩衝區就發送到網絡,也可能在緩衝區中不斷積壓,屢次寫入的數據被一次性發送到網絡,這取決於當時的網絡狀況、當前線程是否空閒等諸多因素,不禁程序員控制。


緩衝區的特性
這些I/O緩衝區特性可整理以下:
I/O緩衝區在每一個TCP套接字中單獨存在;
I/O緩衝區在建立套接字時自動生成;
即便關閉套接字也會繼續傳送輸出緩衝區中遺留的數據;
關閉套接字將丟失輸入緩衝區中的數據。

tcp默認是阻塞的

好比
1.服務器
阻塞接受客戶端鏈接

2.客戶端
同上

參考

unix網絡編程

相關文章
相關標籤/搜索