netty 粘包問題處理


netty 粘包問題處理

key words: netty 粘包 解包 半包 TCP

一般TCP粘包/拆包解決辦法

  1. 定長消息,例如每個報文長度固定,不夠補空格
  2. 使用回車換行符分割,在包尾加上分割符,例如Ftp協議
  3. 消息分割,頭為長度(消息總長度或消息體長度),通常頭用一個int32表示
  4. 復雜的應用層協議

netty的幾種解決方案

特殊分隔符解碼器:DelimiterBasedFrameDecoder

客戶端發送消息

String message = "netty is a nio server framework &"
				+"which enables quick and easy development &"
				+"of net applications such as protocol &"
				+"servers and clients!";

服務端添加解碼器:DelimiterBasedFrameDecoder

ByteBuf delimiter = Unpooled.copiedBuffer("&".getBytes());
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,delimiter));
//1024表示單條消息的最大長度,解碼器在查找分隔符的時候,達到該長度還沒找到的話會拋異常
ch.pipeline().addLast(new StringDecoder());
....
ch.pipeline().addLast(new StringEncoder());

打印輸出:

接收消息:[netty is a nio server framework ]
接收消息:[which enables quick and easy development ]
接收消息:[of net applications such as protocol]
接收消息:[servers and clients!]

參數解釋:

public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf delimiter) {
    this(maxFrameLength, true, delimiter);
}
maxFrameLength:解碼的幀的最大長度

stripDelimiter:解碼時是否去掉分隔符

failFast:為true,當frame長度超過maxFrameLength時立即報TooLongFrameException異常,為false,讀取完整個幀再報異常

delimiter:分隔符

定長解碼器:FixedLengthFrameDecoder

參數說明:

  • frameLength:幀的固定長度

服務端

ch.pipeline().addLast(new FixedLengthFrameDecoder(30));//設置定長解碼器 長度設置為30


public void channelRead(ChannelHandlerContext ctx, Object msg)
        throws Exception {
    System.out.println("接收客戶端msg:["+msg+"]");
    ByteBuf echo=Unpooled.copiedBuffer(MESSAGE.getBytes());
    ctx.writeAndFlush(echo);
}

客戶端

ch.pipeline().addLast(new FixedLengthFrameDecoder(30));//設置定長解碼器

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

參數說明

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

備注:如果長度解析失誤,(過大,就直接丟棄這個包;過小,1、netty不拋出異常;2、校驗通不過)

源碼:
int frameLengthInt = (int) frameLength;
if (in.readableBytes() < frameLengthInt) {
	return null;
}

封包時配合使用LengthFieldPrepender,很容易加上包長度

包頭添加總包長度字節:LengthFieldPrepender

在發布時,自動在幀的頭部加上長度
參數說明:

  • lengthFieldLength:長度屬性的字節長度

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

    應用:
    pipeline.addLast("frameEncode", new LengthFieldPrepender(4, false));

官方說明:

編碼類,自動將
+----------------+	
| "HELLO, WORLD" |	
+----------------+

格式的數據轉換成如下格式的數據,	
+--------+----------------+	
+ 0x000C | "HELLO, WORLD" |	
+--------+----------------+

如果lengthIncludesLengthFieldLength設置為true,則編碼為(多了兩個字節)
+--------+----------------+	
+ 0x000E | "HELLO, WORLD" |	
+--------+----------------+

備注

當時解決問題和記錄時,是查閱了官網和幾篇博客,如果里面內容有copy的地方,請留言url,我會把你的文章引用放到頂上去


免責聲明!

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



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