netty数据解码(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
**/
@Slf4j
public 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);
}
}
运行结果
如果想把长度字段过滤掉:initialBytesToStrip参数传4就可以,把int过滤掉
@Slf4j
public 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);
}
}
运行结果如下图所示:
@Slf4j
public 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
,
免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com