Socket封包、粘包、拆包


socket,如果是做tcp連接,可能會遇到粘包與半包的問題,TCP屬於傳輸層的協議,傳輸層除了有TCP協議外還有UDP協議。那么UDP是否會發生粘包或拆包的現象呢?答案是不會。UDP是基於報文發送的,從UDP的幀結構可以看出,在UDP首部采用了16bit來指示UDP數據報文的長度,因此在應用層能很好的將不同的數據報文區分開,從而避免粘包和拆包的問題。而TCP是基於字節流的,雖然應用層和TCP傳輸層之間的數據交互是大小不等的數據塊,但是TCP把這些數據塊僅僅看成一連串無結構的字節流,沒有邊界;另外從TCP的幀結構也可以看出,在TCP的首部沒有表示數據長度的字段,在使用TCP傳輸數據時,才有粘包或者拆包現象發生的可能。粘包就是多組數據被一並接收了,粘在了一起,無法做划分;半包就是有數據接收不完整,無法處理。要解決粘包、半包的問題,一般在設計數據(消息)格式時會約定好一個字段專門用於描述數據包的長度,這樣就使數據有了邊界,依靠這個邊界,就能把每組數據划分出來,數據不完整時也能獲知數據的缺失。

封包

一般在使用Socket的時候,后台會對Socket傳輸數據有一個自定義的協議,協議可能有些差別不過基本上是大同小異。

Socket發送給服務器的數據,最終要轉換成二進制流數據,並且按照協議約定的格式。

 

eg:消息=消息頭+消息體。消息頭用於描述消息本身的基本信息,消息體則為消息的具體內容

上面這個協議是指我們在發送的數據包頭部開辟一個4個字節長度的空間,用來存儲服務號轉換成的二進制數據。(將1轉換成二進制數據存儲進去占4個字節長度),然后再將數據包長度轉換成二進制數據並存儲到后面開辟的4個字節中(這里需要注意下如果數據要進行加密傳輸,這里的長度應是加密后的長度),最后將數據數據包轉換成二進制數據添加到后面,組成一個完整的數據包也就是封包。這里一定要按協議規定的順序不然服務器解析不了。

粘包、拆包

我們假設在主機A和主機B的應用程序之間有一條TCP連接,主機A有兩條報文D1,D2要發送到B主機,並兩次調用send來發送,每條報文調用一次。

那么,我們自然而然的希望兩條報文是作為兩個獨立的實體,在各自的分組中發送

 

這樣的話,我們無需做任何特別的處理,便能夠很容易的區分每一個獨立的數據,並根據需求分別做相應的處理。但現實往往是有所偏差的,實際的數據傳輸過程很可能不會遵循這個模型。而是會采用以下四種方式之一進行傳輸

實際上,可能的情況還不止4種,這里我們就不做深入了解,以上就是造成粘包的原因。

解決思路:拆包

在上面說到我們給每個數據包添加頭部,頭部中包含數據包的長度,這樣接收到數據后,通過讀取頭部的長度字段,便知道每一個數據包的實際長度了,再根據長度去讀取指定長度的數據便能獲取到正確的數據了。
再來回顧一下 協議:

 

完整的數據包 = 服務號 + 數據包長度 + 數據
數據包頭 = Id(4B) + length(4B) 共占用8字節
數據包 = length(假設占100個字節)
所以這條消息的長度就是108字節可以看到,要想知道一條完整數據的邊界,關鍵就是數據包頭中的length字段

iOS客戶端就需要很好的第三方CocoaAsyncSocket來進行長連接連接和傳輸數據,該第三方地 址:https://github.com/robbiehanson/CocoaAsyncSocket

iOS開發之Socket通信實戰--Request請求數據包編碼模塊

補充博客:初用 CocoaAsyncSocket


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM