關於應用層解決拆包粘包問題?
發送端發送了消息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高?