netty教程12-長度欄位的解碼器LengthFieldBasedFrameDecoder詳解

拆包粘包解決方案3-基於長度欄位的解碼器LengthFieldBasedFrameDecoder

先看LengthFieldBasedFrameDecoder這個類的構造引數:

lengthFieldOffset //長度欄位偏移量 lengthFieldLength // 長度欄位長度 lengthAdjustment // 以長度欄位為基準,還有幾個位元組是內容 initialBytesToStrip // 從頭剝離幾個位元組

1 發訊息時候先發訊息內容的長度 下面的例子 HELLO 5個位元組 WORLD 5個位元組,中間逗號和空格各一個,加起來12, 所以length域值 12,長度欄位佔2個位元組,總共14個位元組 lengthFieldOffset = 0 //長度欄位偏移量 表示長度域從哪裡開始,0表示從頭開始 lengthFieldLength = 2 //長度欄位長度 長度欄位為2說明先讀2個個位元組,讀到 0x000C 知道了訊息的長度為12 lengthAdjustment = 0 initialBytesToStrip = 0 (= do not strip header) BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes) +————+————————+ +————+————————+ | Length | Actual Content |——->| Length | Actual Content | | 0x000C | “HELLO, WORLD” | | 0x000C | “HELLO, WORLD” | +————+————————+ +————+————————+ 2 我不想要長度欄位,想把長度欄位從報文裡剝離出去,用到最後一個欄位 下面的例子 HELLO 5個位元組 WORLD 5個位元組,中間逗號和空格各一個,加起來12,所以length域值 12,長度欄位佔2個位元組,總共14 lengthFieldOffset = 0 //長度欄位偏移量 lengthFieldLength = 2 // 長度欄位長度 lengthAdjustment = 0 // 以長度欄位為基準,還有幾個位元組是內容 initialBytesToStrip = 2 (= the length of the Length field) // 從頭剝離幾個位元組 BEFORE DECODE (14 bytes) AFTER DECODE (12 bytes) +————+————————+ +————————+ | Length | Actual Content |——->| Actual Content | | 0x000C | “HELLO, WORLD” | | “HELLO, WORLD” | +————+————————+ +————————+ 3 Length欄位3個位元組,Header佔一個2個位元組 ,一共佔17個位元組 發訊息時候帶上訊息頭 lengthFieldOffset = 2 (= the length of Header 1) lengthFieldLength = 3 lengthAdjustment = 0 initialBytesToStrip = 0 BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes) +——————+——————+————————+ +——————+——————+————————+ | Header 1 | Length | Actual Content |——->| Header 1 | Length | Actual Content | | 0xCAFE | 0x00000C | “HELLO, WORLD” | | 0xCAFE | 0x00000C | “HELLO, WORLD” | +——————+——————+————————+ +——————+——————+————————+ 4 Length欄位3個位元組,Header佔一個2個位元組 ,一共佔17個位元組,以長度欄位為基準,還有幾個位元組是內容 lengthFieldOffset = 0 lengthFieldLength = 3 lengthAdjustment = 2 (= the length of Header 1) // 以長度欄位為基準,還有幾個位元組是內容 initialBytesToStrip = 0 BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes) +——————+——————+————————+ +——————+——————+————————+ | Length | Header 1 | Actual Content |——->| Length | Header 1 | Actual Content | | 0x00000C | 0xCAFE | “HELLO, WORLD” | | 0x00000C | 0xCAFE | “HELLO, WORLD” | +——————+——————+————————+ +——————+——————+————————+ 5 HDR1,1個位元組 Length 佔2個位元組 ,HDR2 佔1個位元組 ,加上內容12個位元組一共16個位元組 lengthFieldOffset = 1 (= the length of HDR1) // 長度欄位偏移量 lengthFieldLength = 2 //長度欄位長度 lengthAdjustment = 1 (= the length of HDR2) // 以長度欄位為基準,還有幾個位元組是內容 initialBytesToStrip = 3 (= the length of HDR1 + LEN) // 剝離3個位元組 所以剩下 HDR2,Actual Content BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes) +————+————+————+————————+ +————+————————+ | HDR1 | Length | HDR2 | Actual Content |——->| HDR2 | Actual Content | | 0xCA | 0x000C | 0xFE | “HELLO, WORLD” | | 0xFE | “HELLO, WORLD” | +————+————+————+————————+ +————+————————+

上面介紹了LengthFieldBasedFrameDecoder的幾種情況:程式碼:

package com。study。nio。ph2。e6;import io。netty。bootstrap。ServerBootstrap;import io。netty。buffer。ByteBuf;import io。netty。buffer。ByteBufAllocator;import io。netty。channel。AdaptiveRecvByteBufAllocator;import io。netty。channel。ChannelFuture;import io。netty。channel。ChannelInitializer;import io。netty。channel。ChannelOption;import io。netty。channel。embedded。EmbeddedChannel;import io。netty。channel。nio。NioEventLoopGroup;import io。netty。channel。socket。nio。NioServerSocketChannel;import io。netty。channel。socket。nio。NioSocketChannel;import io。netty。handler。codec。LengthFieldBasedFrameDecoder;import io。netty。handler。codec。LineBasedFrameDecoder;import io。netty。handler。logging。LogLevel;import io。netty。handler。logging。LoggingHandler;import lombok。extern。slf4j。Slf4j;/** * @program: isc-study * * @description: 拆包粘包解決方案1——基於長度欄位的解碼器 LengthFieldBasedFrameDecoder 這裡用EmbeddedChannel來測試 * @author: wangjinwei * * 1 發訊息時候先發訊息內容的長度 * 下面的例子 HELLO 5個位元組 WORLD 5個位元組,中間逗號和空格各一個,加起來12,所以length域值 12,長度欄位佔2個位元組,總共14 * lengthFieldOffset = 0 //長度欄位偏移量 表示長度域從哪裡開始,0表示從頭開始 * lengthFieldLength = 2 //長度欄位長度 長度欄位為2說明先讀2個個位元組,讀到 0x000C 知道了訊息的長度為12 * lengthAdjustment = 0 * initialBytesToStrip = 0 (= do not strip header) * * BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes) * +————+————————+ +————+————————+ * | Length | Actual Content |——->| Length | Actual Content | * | 0x000C | “HELLO, WORLD” | | 0x000C | “HELLO, WORLD” | * +————+————————+ +————+————————+ * * 2 我不想要長度欄位,想把長度欄位從報文裡剝離出去,用到最後一個欄位 * 下面的例子 HELLO 5個位元組 WORLD 5個位元組,中間逗號和空格各一個,加起來12,所以length域值 12,長度欄位佔2個位元組,總共14 * lengthFieldOffset = 0 //長度欄位偏移量 * lengthFieldLength = 2 // 長度欄位長度 * lengthAdjustment = 0 // 以長度欄位為基準,還有幾個位元組是內容 * initialBytesToStrip = 2 (= the length of the Length field) // 從頭剝離幾個位元組 * * BEFORE DECODE (14 bytes) AFTER DECODE (12 bytes) * +————+————————+ +————————+ * | Length | Actual Content |——->| Actual Content | * | 0x000C | “HELLO, WORLD” | | “HELLO, WORLD” | * +————+————————+ +————————+ * * 3 Length欄位3個位元組,Header佔一個2個位元組 ,一共佔17個位元組 發訊息時候帶上訊息頭 * lengthFieldOffset = 2 (= the length of Header 1) * lengthFieldLength = 3 * lengthAdjustment = 0 * initialBytesToStrip = 0 * * BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes) * +——————+——————+————————+ +——————+——————+————————+ * | Header 1 | Length | Actual Content |——->| Header 1 | Length | Actual Content | * | 0xCAFE | 0x00000C | “HELLO, WORLD” | | 0xCAFE | 0x00000C | “HELLO, WORLD” | * +——————+——————+————————+ +——————+——————+————————+ * * 4 Length欄位3個位元組,Header佔一個2個位元組 ,一共佔17個位元組,以長度欄位為基準,還有幾個位元組是內容 * lengthFieldOffset = 0 * lengthFieldLength = 3 * lengthAdjustment = 2 (= the length of Header 1) // 以長度欄位為基準,還有幾個位元組是內容 * initialBytesToStrip = 0 * * BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes) * +——————+——————+————————+ +——————+——————+————————+ * | Length | Header 1 | Actual Content |——->| Length | Header 1 | Actual Content | * | 0x00000C | 0xCAFE | “HELLO, WORLD” | | 0x00000C | 0xCAFE | “HELLO, WORLD” | * +——————+——————+————————+ +——————+——————+————————+ * * 5 HDR1,1個位元組 Length 佔2個位元組 ,HDR2 佔1個位元組 ,加上內容12個位元組一共16個位元組 * lengthFieldOffset = 1 (= the length of HDR1) // 長度欄位偏移量 * lengthFieldLength = 2 //長度欄位長度 * lengthAdjustment = 1 (= the length of HDR2) // 以長度欄位為基準,還有幾個位元組是內容 * initialBytesToStrip = 3 (= the length of HDR1 + LEN) // 剝離3個位元組 所以剩下 HDR2,Actual Content * * BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes) * +————+————+————+————————+ +————+————————+ * | HDR1 | Length | HDR2 | Actual Content |——->| HDR2 | Actual Content | * | 0xCA | 0x000C | 0xFE | “HELLO, WORLD” | | 0xFE | “HELLO, WORLD” | * +————+————+————+————————+ +————+————————+ * * @create: 2021-11-02 14:03 **/@Slf4jpublic class Server4 { public static void main(String[] args) { EmbeddedChannel channel = new EmbeddedChannel( new LengthFieldBasedFrameDecoder (1024, 0, 4, 0, 0), new LoggingHandler(LogLevel。INFO) ); ByteBuf buf = ByteBufAllocator。DEFAULT。buffer(); send(buf, “hello, world”); send(buf, “are you ok”); channel。writeAndFlush(buf); } private static void send(ByteBuf buf, String content) { //實際內容 byte[] bytes = content。getBytes(); int len = bytes。length; buf。writeInt(len); buf。writeBytes(bytes); }}

netty教程12-長度欄位的解碼器LengthFieldBasedFrameDecoder詳解

執行結果

如果想把長度欄位過濾掉:initialBytesToStrip引數傳4就可以,把int過濾掉

@Slf4jpublic class Server4 { public static void main(String[] args) { EmbeddedChannel channel = new EmbeddedChannel( new LengthFieldBasedFrameDecoder (1024, 0, 4, 0, 4), new LoggingHandler(LogLevel。INFO) ); ByteBuf buf = ByteBufAllocator。DEFAULT。buffer(); send(buf, “Hello, world”); send(buf, “Hi!”); channel。writeInbound(buf); } private static void send(ByteBuf buf, String content) { //實際內容 byte[] bytes = content。getBytes(); int len = bytes。length; buf。writeInt(len); buf。writeBytes(bytes); }}

執行結果如下圖所示:

netty教程12-長度欄位的解碼器LengthFieldBasedFrameDecoder詳解

@Slf4jpublic class Server4 { public static void main(String[] args) { EmbeddedChannel channel = new EmbeddedChannel( new LengthFieldBasedFrameDecoder (1024, 0, 4, 1, 4), new LoggingHandler(LogLevel。INFO) ); ByteBuf buf = ByteBufAllocator。DEFAULT。buffer(); send(buf, “Hello, world”); send(buf, “Hi!”); channel。writeInbound(buf); } private static void send(ByteBuf buf, String content) { //實際內容 byte[] bytes = content。getBytes(); int len = bytes。length; buf。writeInt(len); //長度後面加了一個位元組 buf。writeByte(1); buf。writeBytes(bytes); }}

執行結果如下,第一個是01

netty教程12-長度欄位的解碼器LengthFieldBasedFrameDecoder詳解