首頁>Club>
5
回覆列表
  • 1 # 現世碼農

    以上圖的協議結構,來舉個粟子:

    1. 我們收到完整的一個協議串為hex碼為:

    232301314731424c35325037545231313535323001000000

    2.對應的拆解一下,各項數值為

    3. 而我們收到粘包的時,或者半包的情況是這樣的。

    3.1 場景1

    一次收到完整兩個資料串:

    232301314731424c35325037545231313535323001000000232301314731424c35325037545231313535323001000000

    3.2 場景2

    一次收到一個半的資料串:

    232301314731424c35325037545231313535323001000000232301314731424c35325037545231

    第二次會收到剩餘的資料串:

    313535323001000000

    4. 透過第三點的我們可以看到我們收到粘包,半包,大概就這些情況。一般處理的方法也很簡單,就透過起始符處理分割一下就可以。這個協議在定義的時候,就考慮到。

    如:遇到場景1 透過起始符進行分割,就可以拆成兩個單獨的包了。

    如:遇到場景2 透過起始符進行分割,可以拿到第一個包。再繼續等待後面的資料包過來,這樣第二個包也就完整了。

    這種具體程式碼中處理的時候,流程大概是這樣:

    4.1 讀取流的資料,

    4.2 檢查資料格式

    4.3 如果滿足一個整包,就丟業務執行緒

    4.4 如果不滿足,將讀標重置為讀取之前就好.(當前也適當的間隔檢索一下緩衝區,以防垃圾包太多過來讓緩衝區爆了,程式掛了)

  • 2 # 困難群眾代表

    設計方案一:定長髮送

    在進行資料傳送時採用固定長度的設計,也就是無論多大資料傳送都分包為固定長度(為便於描述,此處定長為記為LEN),也就是傳送端在傳送資料時都以LEN為長度進行分包。這樣接收方都以固定的LEN進行接收,如此一來發送和接收就能一一對應了。分包的時候不一定能完整的恰好分成多個完整的LEN的包,最後一個包一般都會小於LEN,這時候最後一個包可以在不足的部分填充空白位元組。

    當然,這種方法會有缺陷。1.最後一個包的不足長度被填充為空白部分,也即無效位元組序。那麼接收方可能難以辨別這無效的部分,它本身就是為了補位的,並無實際含義。這就為接收端處理其含義帶來了麻煩。當然也有解決辦法,可以透過增添標誌位的方法來彌補,即在每一個數據包的最前面增加一個定長的報頭,然後將該資料包的末尾標記一併傳送。接收方根據這個標記確認無效位元組序列,從而實現資料的完整接收。2.在傳送包長度隨機分佈的情況下,會造成頻寬浪費。比如傳送長度可能為 1,100,1000,4000位元組等等,則都需要按照定長最大值即4000來發送,資料包小於4000位元組的其他包也會被填充至4000,造成網路負載的無效浪費。

    綜上,此方案適在傳送資料包長度較為穩定(趨於某一固定值)的情況下有較好的效果。

    設計方案二:尾部標記序列

    在每個要傳送的資料包的尾部設定一個特殊的位元組序列,此序列帶有特殊含義,跟字串的結束符標識”\0”一樣的含義,用來標示這個資料包的末尾,接收方可對接收的資料進行分析,透過尾部序列確認資料包的邊界。

    這種方法的缺陷較為明顯:1.接收方需要對資料進行分析,甄別尾部序列。2.尾部序列的確定本身是一個問題。什麼樣的序列可以向”\0”一樣來做一個結束符呢?這個序列必須是不具備通常任何人類或者程式可識別的帶含義的資料序列,就像“\0”是一個無效字串內容,因而可以作為字串的結束標記。那普通的網路通訊中,這個序列是什麼呢?我想一時間很難找到恰當的答案。

    設計方案三:頭部標記分步接收

    這個方法是作者有限學識裡最好的辦法了。它既不損失效率,還完美解決了任何大小的資料包的邊界問題。

    這個方法的實現是這樣的,定義一個使用者報頭,在報頭中註明每次傳送的資料包大小。接收方每次接收時先以報頭的size進行資料讀取,這必然只能讀到一個報頭的資料,從報頭中得到該資料包的資料大小,然後再按照此大小進行再次讀取,就能讀到資料的內容了。這樣一來,每個資料包傳送時都封裝一個報頭,然後接收方分兩次接收一個包,第一次接收報頭,根據報頭大小第二次才接收資料內容。(此處的data[0]的本質是一個指標,指向資料的正文部分,也可以是一篇連續資料區的起始位置。因此可以設計成data[user_size],這樣的話。)

    下面透過一個圖(見附圖)來展現設計思想。

    由圖看出,資料傳送多了封裝報頭的動作;接收方將每個包的接收拆分成了兩次。

    這方案看似精妙,實則也有缺陷:1.報頭雖小,但每個包都需要多封裝sizeof(_data_head)的資料,積累效應也不可完全忽略。2.接收方的接收動作分成了兩次,也就是進行資料讀取的操作被增加了一倍,而資料讀取操作的recv或者read都是系統呼叫,這對核心而言的開銷是一個不能完全忽略的影響,對程式而言效能影響可忽略(系統呼叫的速度非常快)。

    優點:避免了程式設計的複雜性,其有效性便於驗證,對軟體設計的穩定性要求來說更容易達標。綜上,方案三乃上上策!

  • 3 # 匯聚魔杖

    TCP粘包是指傳送方傳送的多個數據包到接收方後粘連在一起,導致資料包不能完整的提現傳送的資料。

    TCP協議

    TCP是一個面向連線的傳輸層協議,不屬於ISO制定的協議集。TCP協議在商業界和工業界的成功應用,使它成為事實上的網路標準,廣泛應用於各種網路主機間的通訊。

    TCP目標是為使用者提供可靠的端到端連線,保證資訊有序無誤的傳輸。TCP為確保可靠性採用了資料編號、校驗和計算、資料確認等一系列措施。

    TCP對傳送的每個資料位元組都進行編號,並請求接收方回傳確認資訊(ACK)。傳送方如果在規定的時間內沒有收到資料確認,就重傳該資料。

    資料編號使接收方能夠處理資料的失序和重複問題。資料誤碼問題透過在每個傳輸的資料段中增加校驗和予以解決,接收方在接收到資料後檢查校驗和,若校驗和有誤,則丟棄該有誤碼的資料段,並要求傳送方重傳。流量控制也是保證可靠性的一個重要措施,若無流控,可能會因接收緩衝區溢位而丟失大量資料,導致許多重傳,造成網路擁塞惡性迴圈。TCP採用可變視窗進行流量控制,由接收方控制傳送方傳送的資料量。

    這些可靠性保障措施為使用者提供了高可靠性的網路傳輸服務,但也影響了傳輸效率。在實際工程應用中,只有關鍵資料的傳輸才採用TCP,而普通資料的傳輸一般採用高效率的UDP。

    UDP不會出現粘包問題。UDP支援的是一對多的模式,不會使用塊的合併最佳化演算法,所以接收端的skbuff(套接字緩衝區)採用了鏈式結構來記錄每一個到達的UDP包,在每個UDP包中就有了訊息頭(包含訊息來源地址,埠等資訊),接收端很容易就能進行區分處理了。

    粘包出現原因

    出現粘包現象的原因有很多方面,它既可能由傳送方造成的,也可能是由接收方造成的。

    傳送方原因

    TCP需要儘可能高效和可靠,預設採用Nagle演算法,傳送方往往要收集到足夠多的資料後合併相連的小資料包,才傳送一包資料,這樣接收方就收到了粘包資料。但接收方並不知曉傳送方合併資料包,並資料包的合併在TCP協議中是沒有分界線的,就會導致接收方不能還原其本來的資料包。

    接收方原因

    TCP是基於“流”的。網路傳輸資料的速度可能會快過接收方處理資料的速度,這時候就會導致,接收方在讀取緩衝區時,緩衝區存在多個數據包。在TCP協議中接收方是一次讀取緩衝區中的所有內容,就不能反映原本的資料資訊。

    粘包情況有兩種:

    一種是粘在一起的包都是完整的資料包;

    一種是粘在一起的包有不完整的包;

    不是所有的粘包現象都需要處理

    如果傳輸的資料為不帶結構的連續流資料(如檔案傳輸),就不必把粘連的包分開(簡稱分包)。但實際工程應用中一般為帶結構的資料,這時就需要做分包處理。

    在處理定長結構資料的粘包問題時,分包演算法比較簡單;

    在處理不定長結構資料的粘包問題時,分包演算法就比較複雜。

    特別是粘在一起的包有不完整的包的粘包情況,一包資料內容被分在了兩個連續的接收包中,處理起來難度較大。實際工程應用中應儘量避免出現粘包現象。

    為了避免粘包現象,可採取以下幾種措施:

    (1)傳送方引起的粘包可透過程式設計設定來避免。如:PUSH標誌是TCP提供了強制資料立即傳送的操作指令,TCP軟體收到該操作指令後,就立即將本段資料傳送出去,而不必等待發送緩衝區滿。

    缺點:雖然可以避免傳送方引起的粘包,但關閉了Negle最佳化演算法,降低了網路傳送效率,影響應用程式的效能,一般不建議使用。

    (2)接收方引起的粘包,可透過最佳化程式設計、精簡接收程序工作量、提高接收程序優先順序等措施來及時接收資料,儘量避免出現粘包現象。

    缺點:只能減少出現粘包的可能性,但並不能完全避免粘包,當傳送頻率較高或某個時間段資料包到達接收方較快,接收方還是有可能來不及接收,導致粘包。

    (3)由接收方控制,將一包資料按結構欄位,人為控制分多次接收,然後合併,透過這種手段來避免粘包。

    缺點:應用程式的效率較低,對實時應用的場合不適合。

    一種比較周全的對策是:接收方建立一預處理執行緒,對接收到的資料包進行預處理,將粘連的包分開。另外,普通資料的傳輸採用UDP,而重要的資料採用TCP。由於UDP不是面向‘流’的,而且UDP是具有訊息邊界的。也就是說UDP的傳送的每一個數據包都是獨立的。所以UDP並不存在粘包的問題。

  • 4 # 一個程式設計師的奮鬥史

    回答問題之前,先來糾正題主的一個錯誤概念,TCP是一種基於位元組流的協議,根本不存在所謂的“包”,更不必說粘包。我想題主應該想問:TCP傳輸協議下,應用層資料傳送和接收問題。

    官方文件說的已經很清楚了,send和recv的返回值表示成功傳送/接收的位元組數。

    man send

    man recv

    此時,不僅不會有「粘包」的錯覺,甚至如何解決都知道了。資料沒發完?繼續發唄;沒收完?繼續收就行了。那麼如何知道資料沒有收完呢?其實約定個特殊字元作為結束符(比如HTTP協議以\r\n\r\n作為結束標誌)或者提前約定好資料長度就可以了。

    例項:

    下面看看專案開發中常用的方法,「設定定長訊息」。先收取一個固定大小的包頭資訊,接著根據包頭裡面指定的包體大小來收取包體大小。具體的程式碼示例如下所示:

    程式設計師,一定要多看官方文件!多看官方文件!多看官方文件!這可比某些所謂的部落格要嚴謹的多。

  • 5 # 鬧市一俗人

    要靠訊息封裝來解決,約定定長的訊息頭,訊息頭內標明訊息資料長度。接收端透過訊息快取和訊息頭來完成訊息提取。

  • 中秋節和大豐收的關聯?
  • 怎麼樣可以增發?