socket的半包,粘包與分包的問題

2021-07-05 04:24:51 字數 2691 閱讀 2125

首先看兩個概念: 

短連線:

連線->傳輸資料->關閉連線 

http是無狀態的,瀏覽器和伺服器每進行一次http操作,就建立一次連線,但任務結束就中斷連線。 

也可以這樣說:短連線是指socket連線後傳送後接收完資料後馬上斷開連線。 

長連線:

連線->傳輸資料->保持連線 -> 傳輸資料-> 。。。 ->關閉連線。 

長連線指建立socket連線後不管是否使用都保持連線,但安全性較差。 

之所以出現粘包和半包現象,是因為tcp當中,只有流的概念,沒有包的概念.

半包

指接受方沒有接受到乙個完整的包,只接受了部分,這種情況主要是由於tcp為提高傳輸效率,將乙個包分配的足夠大,導致接受方並不能一次接受完。(在長連線和短連線中都會出現)。

粘包與分包

指傳送方傳送的若干包資料到接收方接收時粘成一包,從接收緩衝區看,後一包資料的頭緊接著前一包資料的尾。出現粘包現象的原因是多方面的,它既可能由傳送方造成,也可能由接收方造成。傳送方引起的粘包是由tcp協議本身造成的,tcp為提高傳輸效率,傳送方往往要收集到足夠多的資料後才傳送一包資料。若連續幾次傳送的資料都很少,通常tcp會根據優化演算法把這些資料合成一包後一次傳送出去,這樣接收方就收到了粘包資料。接收方引起的粘包是由於接收方使用者程序不及時接收資料,從而導致粘包現象。這是因為接收方先把收到的資料放在系統接收緩衝區,使用者程序從該緩衝區取資料,若下一包資料到達時前一包資料尚未被使用者程序取走,則下一包資料放到系統接收緩衝區時就接到前一包資料之後,而使用者程序根據預先設定的緩衝區大小從系統接收緩衝區取資料,這樣就一次取到了多包資料。分包是指在出現粘包的時候我們的接收方要進行分包處理。(在長連線中都會出現)

什麼時候需要考慮半包的情況?

從備註中我們了解到socket內部預設的收發緩衝區大小大概是8k,但是我們在實際中往往需要考慮效率問題,重新配置了這個值,來達到系統的最佳狀態。 

乙個實際中的例子:用mina作為伺服器端,使用的快取大小為10k,這裡使用的是短連線,所有不用考慮粘包的問題。 

問題描述:在併發量比較大的情況下,就會出現一次接受並不能完整的獲取所有的資料。 

處理方式: 

1.通過包頭+包長+包體的協議形式,當伺服器端獲取到指定的包長時才說明獲取完整。 

2.指定包的結束標識,這樣當我們獲取到指定的標識時,說明包獲取完整。 

什麼時候需要考慮粘包的情況?

1.當時短連線的情況下,不用考慮粘包的情況 

2.如果傳送資料無結構,如檔案傳輸,這樣傳送方只管傳送,接收方只管接收儲存就ok,也不用考慮粘包 

3.如果雙方建立連線,需要在連線後一段時間內傳送不同結構資料 

處理方式: 

接收方建立一預處理執行緒,對接收到的資料報進行預處理,將粘連的包分開 

注:粘包情況有兩種,一種是粘在一起的包都是完整的資料報,另一種情況是粘在一起的包有不完整的包

備註: 

乙個包沒有固定長度,乙太網限制在46-1500位元組,1500就是乙太網的mtu,超過這個量,tcp會為ip資料報設定偏移量進行分片傳輸,現在一般可允許應用層設定8k(ntfs系)的緩衝區,8k的資料由底層分片,而應用看來只是一次傳送。windows的緩衝區經驗值是4k,socket本身分為兩種,流(tcp)和資料報(udp),你的問題針對這兩種不同使用而結論不一樣。甚至還和你是用阻塞、還是非阻塞socket來程式設計有關。 

1、通訊長度,這個是你自己決定的,沒有系統強迫你要發多大的包,實際應該根據需求和網路狀況來決定。對於tcp,這個長度可以大點,但要知道,socket內部預設的收發緩衝區大小大概是8k,你可以用setsockopt來改變。但對於udp,就不要太大,一般在1024至10k。注意一點,你無論發多大的包,ip層和鏈路層都會把你的包進行分片傳送,一般區域網就是1500左右,廣域網就只有幾十位元組。分片後的包將經過不同的路由到達接收方,對於udp而言,要是其中乙個分片丟失,那麼接收方的ip層將把整個傳送包丟棄,這就形成丟包。顯然,要是乙個udp發包佷大,它被分片後,鏈路層丟失分片的機率就佷大,你這個udp包,就佷容易丟失,但是太小又影響效率。最好可以配置這個值,以根據不同的環境來調整到最佳狀態。 

send()函式返回了實際傳送的長度,在網路不斷的情況下,它絕不會返回(傳送失敗的)錯誤,最多就是返回0。對於tcp你可以位元組寫乙個迴圈傳送。當send函式返回socket_error時,才標誌著有錯誤。但對於udp,你不要寫迴圈傳送,否則將給你的接收帶來極大的麻煩。所以udp需要用setsockopt來改變socket內部buffer的大小,以能容納你的發包。明確一點,tcp作為流,發包是不會整包到達的,而是源源不斷的到,那接收方就必須組包。而udp作為訊息或資料報,它一定是整包到達接收方。 

2、關於接收,一般的發包都有包邊界,首要的就是你這個包的長度要讓接收方知道,於是就有個包頭資訊,對於tcp,接收方先收這個包頭資訊,然後再收包資料。一次收齊整個包也可以,可要對結果是否收齊進行驗證。這也就完成了組包過程。udp,那你只能整包接收了。要是你提供的接收buffer過小,tcp將返回實際接收的長度,餘下的還可以收,而udp不同的是,餘下的資料被丟棄並返回wsaemsgsize錯誤。注意tcp,要是你提供的buffer佷大,那麼可能收到的就是多個發包,你必須分離它們,還有就是當buffer太小,而一次收不完socket內部的資料,那麼socket接收事件(onreceive),可能不會再觸發,使用事件方式進行接收時,密切注意這點。這些特性就是體現了流和資料報的區別。 

參照:

Socket的半包,粘包與分包的問題

概述 關於半包 粘包和分包的現象產生,是因為tcp當中只有流的概念,沒有包的概念.而面向流的通訊是無訊息保護邊界的。由於tcp無訊息保護邊界,需要在訊息接收端處理訊息邊界問題,因此自然產生了如何分包。半包 指接受方沒有接受到乙個完整的包,只接受了部分,這種情況主要是由於tcp為提高傳輸效率,將乙個包...

Socket粘包分包

粘包和分包問題 1.首先什麼是包 包就是每次伺服器向客戶端傳送的資料每傳送乙個訊息都會被打成乙個包傳送到客戶端。客戶端向伺服器端傳送訊息也是一樣的。2.為什麼會有粘包和分包的問題 是因為sockettcp自身的優化機制所導致的。3.什麼是粘包 粘包就是當伺服器端傳送的資料很小的時候又很頻繁的時候,就...

IOS 詳解socket程式設計 oc 粘包 半包處理

ios 詳解socket程式設計 oc 粘包 半包處理 在做socket程式設計時,如果是做tcp連線,那就不可避免的會遇到粘包與半包的問題,粘包就是多組資料被一併接收了,粘在了一起,無法做劃分 半包就是有資料接收不完整,無法處理。要解決粘包 半包的問題,一般在設計資料 訊息 格式時會約定好乙個字段...