1. 程式人生 > >TCP 出現分包粘包的原因 以及解決辦法

TCP 出現分包粘包的原因 以及解決辦法

轉載:

粘包產生原因:

先說TCP:由於TCP協議本身的機制(面向連線的可靠地協議-三次握手機制)客戶端與伺服器會維持一個連線(Channel),資料在連線不斷開的情況下,可以持續不斷地將多個數據包發往伺服器,但是如果傳送的網路資料包太小,那麼他本身會啟用Nagle演算法(可配置是否啟用)對較小的資料包進行合併(基於此,TCP的網路延遲要UDP的高些)然後再發送(超時或者包大小足夠)。那麼這樣的話,伺服器在接收到訊息(資料流)的時候就無法區分哪些資料包是客戶端自己分開發送的,這樣產生了粘包;伺服器在接收到資料庫後,放到緩衝區中,如果訊息沒有被及時從快取區取走,下次在取資料的時候可能就會出現一次取出多個數據包的情況,造成粘包現象(確切來講,對於基於TCP協議的應用,不應用包來
描述,而應 用 流來描述),個人認為伺服器接收端產生的粘包應該與linux核心處理socket的方式 select輪詢機制的線性掃描頻度無關。 再說UDP:本身作為無連線的不可靠的傳輸協議(適合頻繁傳送較小的資料包),他不會對資料包進行合併傳送(也就沒有Nagle演算法之說了),他直接是一端傳送什麼資料,直接就發出去了,既然他不會對資料合併,每一個數據包都是完整的(資料+UDP頭+IP頭等等發一次資料封裝一次)也就沒有粘包一說了。 分包產生的原因就簡單的多:可能是IP分片傳輸導致的,也可能是傳輸過程中丟失部分包導致出現的半包,還有可能就是一個包可能被分成了兩次傳輸,在取資料的時候,先取到了一部分(還可能與接收的緩衝區大小有關係),總之就是一個數據包被分成了多次接收。 解決辦法: 粘包與分包的處理方法: 我根據現有的一些開源資料做了如下總結(常用的解決方案): 一個是採用分隔符的方式,即我們在封裝要傳輸的資料包的時候,採用固定的符號作為結尾符(資料中不能含結尾符),這樣我們接收到資料後,如果出現結尾標識,即人為的將粘包分開,如果一個包中沒有出現結尾符,認為出現了分包,則等待下個包中出現後 組合成一個完整的資料包,這種方式適合於文字傳輸的資料,如採用/r/n之類的分隔符; 另一種是採用在資料包中新增長度的方式,即在資料包中的固定位置封裝資料包的長度資訊(或可計算資料包總長度的資訊),伺服器接收到資料後,先是解析包長度,然後根據包長度擷取資料包(此種方式常出現於自定義協議中),但是有個小問題就是如果客戶端第一個資料包資料長度封裝的有錯誤,那麼很可能就會導致後面接收到的所有資料包都解析出錯(由於TCP建立連線後流式傳輸機制),只有客戶端關閉連線後重新開啟才可以消除此問題,我在處理這個問題的時候對資料長度做了校驗,會適時的對接收到的有問題的包進行人為的丟棄處理(客戶端有自動重發機制,故而在應用層不會導致資料的不完整性); 另一種不建議的方式是TCP採用短連線處理粘包(這個得根據需要來,所以不建議); /***********************************************************************************************************************************************/

TCP粘包是指傳送方傳送的若干包資料到接收方接收時粘成一包,從接收緩衝區看,後一包資料的頭緊接著前一包資料的尾。粘包可能由傳送方造成,也可能由接收方造成。TCP為提高傳輸效率,傳送方往往要收集到足夠多的資料後才傳送一包資料,造成多個數據包的粘連。如果接收程序不及時接收資料,已收到的資料就放在系統接收緩衝區,使用者程序讀取資料時就可能同時讀到多個數據包。因為系統傳輸的資料是帶結構的資料,需要做分包處理。

為了適應高速複雜網路條件,我們設計實現了粘包處理模組,由接收方通過預處理過程,對接收到的資料包進行預處理,將粘連的包分開。為了方便粘包處理,提高處理效率,在接收環節使用了環形緩衝區來儲存接收到的資料。其結構如表1所示。

                                                            環形緩衝結構

欄位名

型別

含義

CS

CRITICAL_SECTION

保護環形緩衝的臨界區

pRingBuf

UINT8*

緩衝區起始位置

pRead

UINT8*

當前未處理資料的起始位置

pWrite

UINT8*

當前未處理資料的結束位置

pLastWrite

UINT8*

當前緩衝區的結束位置

環形緩衝跟每個TCP套接字繫結。在每個TCPSOCKET_OBJ建立時,同時建立一個PRINGBUFFER結構並初始化。這時候,pRingBuf指向環形緩衝區的記憶體首地址,pReadpWrite指標也指向它。pLastWrite指標在這時候沒有實際意義。初始化之後的結構如圖1所示。


初始化後的環形緩衝區

在每次投遞一個TCP的接收操作時,從RINGBUFFER獲取記憶體作接收緩衝區,一般規定一個最大值L1作為可以寫入的最大資料量。這時把pWrite的值賦給BUFFER_OBJbuf欄位,把L1賦給bufLen欄位。這樣每次接收到的資料就從pWrite開始寫入緩衝區,最多寫入L1位元組,如圖 2


2分配緩衝後的環形緩衝

如果某次分配過程中,pWrite到緩衝區結束的位置pEnd長度不夠最小分配長度L1,為了提高接收效率,直接廢棄最後一段記憶體,標記pLastWritepWrite。然後從pRingBuf開始分配記憶體,如圖 3


使用到結尾的環形緩衝

特殊情況下,如果處理包速度太慢,或者接收太快,可能導致未處理包占用大部分緩衝區,沒有足夠的緩衝區分配給新的接收操作,如圖4。這時候直接報告錯誤即可。


4沒有足夠接收緩衝的環形緩衝

當收到一個長度為L資料包時,需要修改緩衝區的指標。這時候已經寫入資料的位置變為(pWrite+L),如圖 5


5收到長度為L的資料的環形緩衝

分析上述環形緩衝的使用過程,收到資料後的情況可以簡單歸納為兩種:pWrite>pRead,接收但未處理的資料位於pReadpWrite之間的緩衝區;pWrite<pRead,這時候,資料位於pReadpLastWritepRingbufpWrite之間。這兩種情況分別對應圖6、圖 7

首先分析圖6。此時,pRead是一個包的起始位置,如果L1足夠一個包頭長度,就獲取該包的長度資訊,記為L。假如L1>L,就說明一個數據包接收完成,根據包型別處理包,然後修改pRead指標,指向下一個包的起始位置(pRead+L)。這時候仍然類似於之前的狀態,於是解包繼續,直到L1不足一個包的長度,或者不足包頭長度。這時退出解包過程,等待後續的資料到來。


6有未處理資料的環形緩衝(1


7有未處理資料的環形緩衝(2

 8稍微複雜。首先按照上述過程處理L1部分。存在一種情況,經過若干個包處理之後,L1不足一個包,或者不足一個包頭。如果這時(L1+L2)足夠一個包的長度,就需要繼續處理。另外申請一個最大包長度的記憶體區pTemp,把L1部分和L2的一部分複製到pTemp,然後執行解包過程。

經過上述解包之後,pRead就轉向pRingBufpWrite之間的某個位置,從而回歸情況圖 6,繼續按照圖 6部分執行解包。

相關推薦

TCP 出現分包原因 以及解決辦法

轉載: 粘包產生原因: 先說TCP:由於TCP協議本身的機制(面向連線的可靠地協議-三次握手機制)客戶端與伺服器會維持一個連線(Channel),資料在連線不斷開的情況下,可以持續不斷地將多個數據包發往伺服器,但是如果傳送的網路資料包太小,那麼他本身會啟用Nagle演算

Unity Socket傳輸 TCP和拆原因以及解決策略

3. 乙太網的payload大於MTU進行IP分片。MTU指:一種通訊協議的某一層上面所能通過的最大資料包大小。如果IP層有一個數據包要傳,而且資料的長度比鏈路層的MTU大,那麼IP層就會進行分片,把資料包分成若干片,讓每一片都不超過MTU。注意,IP分片可以發生在原始傳送端主機上,也可以發生在中間路由器上

tcp協議產生-問題的解決方案

import col 執行 port == pan 字符 use utf8 客戶端 1 客戶端 2 3 from socket import * 4 import json,struct 5 6 7 client=socket(AF_INET,SOCK

基於tcp協議下現象和解決方案

一、緩衝區   每個 socket 被建立後,都會分配兩個緩衝區,輸入緩衝區和輸出緩衝區。write()/send() 並不立即向網路中傳輸資料,而是先將資料寫入緩衝區中,再由TCP協議將資料從緩衝區傳送到目標機器。一旦將資料寫入到緩衝區,函式就可以成功返回,不管它們有沒有到達目標機器,也不管它們何時被髮送

我的Python成長之路---Day33-TCP套接中的現象和解決辦法

一、什麼是粘包現象 首先我們先來基於TCP製作一個執行遠端命令的程式 注意:在服務端使用subprocess執行系統命令返回結果的候 res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stderr=sub

導航欄載入時可能出現閃的原因以及解決辦法

元素閃爍很醜,難解決。 修改 Class 而不是 Style 我在不久前做過一個導航欄,要求其滾動到螢幕頂端後固定。很常見。開始的時候沒問題,很快就可以搞定。 nav { position: absolute; top: 60px; } var scroll

使用myeclipse出現中文亂碼的情況以及解決辦法

gb2312 編碼格式 myeclipse 屬性 編碼方式 connect XML 顯示 .class 一:在jsp頁面使用中文在瀏覽器中顯示的時候出現亂碼,解決問題的辦法: 1)直接在<mete>標簽中修改charset屬性為"utf-8"或者為“gb23

ssh 登錄出現的幾種錯誤以及解決辦法

chang port apt-get his down ssi mis pan 其他 首先、確保server端的ssh服務是開的(service shhd start) 然後在client端輸入: ssh usrname@serverip (遠程登錄) scp filena

JSON.NET的Self referencing loop detected with type的原因以及解決辦法

從數據 xml序列化器 信息 不起作用 hand href creat server HR 模型中有循環引用是很常見的。例如,以下模型顯示雙向導航屬性: 1: public class Category 2: { 3: public Cate

/var/spool/postfix/maildrop 出現大量文件原因解決辦法

完全 通過 解決問題 輸出內容 .sh uil 問題 立即生效 pos 今天發現服務器硬盤報警,出現空間不足的情況,後經查看發現是 /var/spool/postfix/maildrop 有大量文件,但服務器本身沒有啟動 postfix服務。 繼續上網查資料,發現是cron

EF關於報錯Self referencing loop detected with type的原因以及解決辦法

content handle check new ren calc and cal str 1)具體報錯 { "Message": "出現錯誤。", "ExceptionMessage": "“ObjectContent`1”類型未能序列化內容類型“app

Nginx伺服器出現502錯誤的原因解決辦法總結

  一些執行在Nginx上的網站有時候會出現“502 Bad Gateway”錯誤,有些時候甚至頻繁的出現。有些站長是在剛剛轉移到Nginx之後就出現了這個問題,所以經常會懷疑這是不是Nginx的問題,但事實上這是個誤區。 以下是從張宴和Ayou的部落格蒐集整理的一些Ngin

cas單點登入遇到 supplied credentials: [admin+password] 問題原因以及解決辦法

最近在寫shiro-cas單點登入demo的時候,真的是問題多多,百度了半天也沒有什麼有用的資訊,服務端部署了一遍又一遍,真的sui(抱怨over..哈哈) 我遇到的問題是服務端配置資料庫連線的時候出現的問題,先回顧一下流程 一、修改deployerConfigContext.xm

SuppressLint黃色警告的原因以及解決辦法

最近在做專案的時候,碰到方法的前面和類的前面有時會出現@SuppressLint或者@SuppressWarnings這樣的黃色警告,看起來很不舒服,於是上網蒐集了一些相關資料。發現這些警告的出現其實是由於我們編寫程式碼時的一些不規範的寫法導致,解決這些問題其實是能提高我們程

unsupported message type: DefaultFullHttpResponse (expected: ByteBuf, FileRegion) 原因以及解決辦法

使用netty做http伺服器的時候 用android連結 會出現這個錯誤 原因是http-aggregator順序有問題 (ps:目前大部分國內部落格都是這個排序有點坑爹): package com.sencorsta.ids.httptest; impor

設定padding會把div撐開的原因以及解決辦法

因為Div添加了內邊距屬性,Div的實際寬度=Div的初始固定值+邊距值(高度同理) 例如Div設定為寬度為100px,新增5px的上下左右內邊距,該Div的最後寬度為100+5+5=110px。 CSS padding 屬性定義元素邊框與元素內容之間的空白區

SQL Server中事務日誌已滿的原因以及解決辦法

錯誤描述:資料庫的事務日誌已滿。若要查明無法重用日誌中的空間的原因 ,請參閱sys.databases 中的 log_reuse_wait_desc 列 。   首先引入一下事務日誌的概念(來自百度百科):   事務日誌是一個與資料庫檔案分開的檔案

vue專案出現空格警告的原因及其解決辦法

原因: 因為你的Webpack 配置中大概是使用了 eslint-loader,這是用來規範程式碼風格的,在多人協作或大專案中推薦使用,不想要則可以在 webpack.config.js 中去掉。eslint是語法檢查工具,但限制太過於嚴格,作為開發人員,大部分人還是無法適應這

ssh 登入出現的幾種錯誤以及解決辦法

首先確保要登入的主機安裝了openssh-client(ubuntu有預設安裝,如果沒有則sudo apt-get install openssh-client),如果要使本機開放SSH服務就需要安裝 openssh-server sudo apt-get install openssh-server   

mysql佔用伺服器cpu過高的原因以及解決辦法

排查方法 : > mysql -uroot -p      #登陸資料庫 >********       &n