netty 粘包問題處理

netty 粘包問題處理

key words: netty 粘包 解包 半包 TCPapp

通常TCP粘包/拆包解決辦法

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

netty的幾種解決方案

特殊分隔符解碼器:DelimiterBasedFrameDecoder

客戶端發送消息tcp

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());

打印輸出:ui

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

參數解釋:this

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

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

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

delimiter:分隔符

定長解碼器:FixedLengthFrameDecoder

參數說明:編碼

  • frameLength:幀的固定長度

服務端url

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);
}

客戶端netty

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

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

參數說明code

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

備註:若是長度解析失誤,(過大,就直接丟棄這個包;太小,一、netty不拋出異常;二、校驗通不過)server

源碼:
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,我會把你的文章引用放到頂上去

相關文章
相關標籤/搜索