標籤:

關於應用層解決拆包粘包問題?

發送端發送了消息A(4位元組),消息B(8位元組),消息C(10位元組),到了接收端,應用層業務收到的是2位元組,12位元組,8位元組,這個問題該怎麼解決,貌似只能用遞歸解決呢?


TCP通信機制的底層的粘包和拆包:

當我們在接收消息的時候,顯示不能認為讀取到的報文就是個整包消息。

如何區分一個整包消息,通常有如下五種做法:

1) 固定長度,例如每xxx 個位元組代表一個整包消息,不足的前面補位。解碼器在處理這類定常消息的時候比較簡單,每次讀到指定長度的位元組後再進行解碼;

2) 通過回車換行符區分消息,例如HTTP協議。這類區分消息的方式多用於文本協議;

3) 通過特定的分隔符區分整包消息;

4) 通過在協議頭/消息頭中設置長度欄位來標識整包消息。

5)通過協議號來確定解析協議的長度,由一個唯一協議標記就可以確定一個反序列化類,從而實現區分整包


1. 拆包粘包問題不應該是應用層關心的, 這個在協議層已經解決了

2. 從socket里讀到的data是數據流, 多少都是不確定的, 要根據自己的協議去處理。

* 沒有讀到足夠的數據, 等會繼續讀

* 讀到足夠數據, 協議解析完畢, 把數據傳給上層應用

涉及到netty, 繼承ByteToMessageDecoder並實現decode method. 可參照https://github.com/netty/netty/blob/4.1/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java

你關心的issue, ByteToMessageDecoder內部已經給你搞定了


舉個例子,我目前在寫一個N年前的老網遊的離線掛,它的協議是這樣的:

前2個位元組代表一個包的開頭,第3、4兩個位元組代表該包後面的數據長度,第5、6兩個位元組代表該包的類別(比如是登錄,還是聊天,還是戰鬥等),後面的位元組和具體的類別有關。

客戶端讀取的時候,先讀2個位元組,讀到包的開頭;然後再讀2個位元組,獲取後續數據長度,長度為N;最後再讀N個位元組。然後再處理。


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


這個問題不需要糾結,只要你是基於tcp的就會面臨這個問題,就兩種解決辦法

1.在不確定長度的欄位前加上確定長度的欄位

2.不同消息間有特殊分隔符,比如http是r/n/r/n/

1.方法簡單實用效率高,一般都用此方法

2.效率肯定低,因為每個字元都要解析,不知道有啥優點,但是http用了,我也沒弄懂為什麼要這麼用,請大神指點


剛好碰巧我遇見過這樣很不友好的客戶端程序員。他一定要用ltv堅決不用序列化。

所以我在Netty上專門給他加了一個TCP的handler,可以用各種姿勢滿足他的要求。針對LTV,netty4有專門的解碼器,前面有同學說用ByteToMessageDecoder也可以,不過你用我推薦的這個或許會好一點。因為有的技術團隊真的不懂序列化是什麼東西

參考我在CSDN上貼的一篇文章http://blog.csdn.net/arctan90/article/details/51244443

手把手教你怎麼收,怎麼發LTV


看題目分類到netty,netty有專門的編碼和解碼器

如果不是netty,那麼自己要分包,ltv(length,type,value)包頭定義長度,類型,值


面向流的協議的自然特性而已,在《Netty實戰》這本書中重點講解了如何使用Netty提供的編解碼器對基於長度的和基於分隔符的協議進行解析。希望可以幫到您。

您可以購買我翻譯的《Netty實戰》這本書。

如何評價《Netty實戰》這本書? - 知乎

京東預售鏈接(優先發貨):《Netty實戰》([美]諾曼·毛瑞爾(Norman Maurer),馬文·艾倫·沃爾夫泰爾(Marvin Allen Wolfthal))

親,看下這個書就知道了,使用Codec啊,10章11章。


推薦閱讀:

通俗地講,Netty 能做什麼?
Netty的Reactor線程組中的每個Reactor線程處理網路I/O為什麼一定要串列化,並行化不是更能提高系統吞吐量嗎?
為什麼nio效率會比bio高?

TAG:TCP | Netty |