Netty 對通訊協議結構設計的啟發和總結


Netty 通訊協議結構設計的總結

key words: 通信,協議,結構設計,netty,解碼器,LengthFieldBasedFrameDecoder

原創

包含與機器/設備的通訊協議結構的設計,安全性,數據有效性的設計思路記錄

通訊協議結構選擇

按照解決TCP粘包的解決方案的協議設計思路,大部分情況也就是:

  1. 定長消息,每個報文固定長度,不夠補0或其他
  2. 用特殊字符/字節做分割符,遇到分隔符拆包
  3. 不定長報文,包頭帶長度,以長度字節為准進行消息分割

每種處理方式都有不同的適用場景(例如 方法2適合文本傳輸過程中的拆包,卻不適合byte[]數據的拆包),方法1,2,3在netty里面得到了很好的支持,具體可以見詳見netty 在TCP粘包問題處理這篇文章

對於物聯通訊來說,傳輸是最佳數據類型,所以方法3是比較合適的,這就要求通訊協議在設計時,需要把報文長度放在最前面,下面看看netty自帶的基於包頭不定長的解碼器,能省去自己解決粘包的時間,把關注點放到業務數據的處理上

基於包頭不固定長度的解碼器:LengthFieldBasedFrameDecoder

LengthFieldBasedFrameDecoder參數說明

  • maxFrameLength:解碼的幀的最大長度
  • lengthFieldOffset:長度屬性的起始位(偏移位),包中存放有整個大數據包長度的字節,這段字節的起始位置
  • lengthFieldLength:長度屬性的長度,即存放整個大數據包長度的字節所占的長度
  • lengthAdjustmen:長度調節值,在總長被定義為包含包頭長度時,修正信息長度。
  • initialBytesToStrip:跳過的字節數,根據需要我們跳過lengthFieldLength個字節,以便接收端直接接受到不含“長度屬性”的內容
  • failFast :為true,當frame長度超過maxFrameLength時立即報TooLongFrameException異常,為false,讀取完整個幀再報異常

有了這個解碼器,就很輕松完成拆包工作,拆出來的業務數據,再交由下一個decoder handler處理

那么封包呢?封包也不用自己加長度,直接在ChannelPipeline的最后加上LengthFieldPrepender 編碼器

LengthFieldPrepender 編碼器

參數說明:

  • lengthFieldLength:長度屬性的字節長度
  • lengthIncludesLengthFieldLength:false,長度字節不算在總長度中,true,算到總長度中

配合使用LengthFieldPrepender,很容易就完成了,這樣在flush前,netty自動會為報文加上一個length。

需要注意的是,在業務處理器里面要響應write時,請用pipeline.write,如果直接用ctx.write,最后報文就不會加長度,因為不會進入到LengthFieldPrepender編碼器中去

示例代碼:

@Component("MyChannelInit")
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {

	@Override
	protected void initChannel(SocketChannel channel) throws Exception {
		ChannelPipeline pipeline = channel.pipeline();
		//pipeline.addLast(new LoggingHandler(LogLevel.INFO));
		pipeline.addLast("frameDecode",new LengthFieldBasedFrameDecoder(1024,0,2,-2,2));
		pipeline.addLast("decoder", new MyDataDecoder());//in 1
		pipeline.addLast("handler", new MyInboundHandler());//in 2

		pipeline.addLast(new IdleStateHandler(40, 0, 0));//out3
		//pipeline.addLast("encoder", new DataEncoder());//out2
		pipeline.addLast("frameEncode",new LengthFieldPrepender(2,true));//out1
	}
}

收到設備發過來的數據,new LengthFieldBasedFrameDecoder(1024,0,2,-2,2):
解碼最大長度1024,起始偏移0,長度參數占字節數2,總長包含長度字節數,修正長度-2,傳輸到下一個Decoder時數據,跳過字節數2(也就是不帶長度)

發送到設備的數據,new LengthFieldPrepender(2,true),自動加上兩個字節的長度

通訊數據的保密性

如果不想讓人攔截有效數據和入侵破壞,通訊數據最好還是帶上加密,最好還是動態的,不要說什么MD5 RSA DES之類的,終端硬件那邊處理器性能沒那么強悍,服務端這邊也影響性能

所以,哪怕就是個簡單的加解密,也足夠讓90%的人知難而退(大部分人沒事突突你干嘛(-__-)b)

我們當時設計了token機制,token有時效性,每隔一段時間就需要從服務端獲取新的token值,而數據解密的參數就跟token有關,這樣就算拿着數據去分析規律,由於token值時不時變換,導致解密方法和解密參數都不一樣,這樣也許能起到部分作用

因為token,一段時間就會失效,所以我們就有一條專門獲取token的指令,為了保證協議的一致性(凡是數據傳遞都需要token),所以訂了一個特殊的token和特殊的加解密,這樣可以保證獲取token獲取能夠通過程序的辨識

所以數據結構的頭 為: LENGTH + TOKEN

通訊數據的有效性驗證

對於高要求的的數據傳輸,是否有必要進行校驗,CRC16 CRC32校驗應該就夠了
數據結構的頭 :LENGTH + TOKEN + CRC + DATA

后面的就是具體傳輸的數據的處理


免責聲明!

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



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