1.作用
- LengthFieldBasedFrameDecoder 解码器根据消息中的length字段的值动态拆分接收到的ByteBuf,在解码二进制报文(包含报文头:报文头存储报文的长度)时,此解码器特别有用
2.参数解析
2.1搭建简单的netty通讯
2.1.1client
package cn.itbin.decode.client;
import cn.itbin.client.TelnetClientInitializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* @author chenxiaogao
* @className Client
* @description TODO
* @date 2020/11/7
**/
public class Client {
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8023"));
public static void main(String[] args) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
;
// 开始尝试连接
Channel ch = b.connect(HOST, PORT).sync().channel();
String request = "HELLO, WORLD";
ByteBuf buffer = Unpooled.buffer();
buffer.writeShort(request.length());
buffer.writeBytes(request.getBytes());
ch.writeAndFlush(buffer);
} finally {
group.shutdownGracefully();
}
}
}
2.1.2server
- server
package cn.itbin.decode.server;
import cn.itbin.server.TelnetServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.util.Arrays;
/**
* @author chenxiaogao
* @className Server
* @description TODO
* @date 2020/11/7
**/
public class Server {
static final int PORT = Integer.parseInt(System.getProperty("port", "8023"));
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ServerInitializer());
b.bind(PORT).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
- ServerInitializer
package cn.itbin.decode.server;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
/**
* @author chenxiaogao
* @className ServerInitializer
* @description TODO
* @date 2020/11/7
**/
public class ServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2));
pipeline.addLast(new ServerHandler());
}
}
- ServerHandler
package cn.itbin.decode.server;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.nio.charset.Charset;
/**
* @author chenxiaogao
* @className ServerHandler
* @description TODO
* @date 2020/11/7
**/
public class ServerHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
int length = msg.readableBytes();
System.out.println("请求总长度 = " + length);
//首先读取两个字节的长度位
short len = msg.readShort();
System.out.println("请求报文体的长度 = " + len);
System.out.println("读取两个字节后的ByteBuf = " + msg);
//读取报文内容
byte[] bytes = new byte[len];
msg.readBytes(bytes, 0, len);
String request = new String(bytes, Charset.forName("GBK"));
System.out.println("请求报文体 = " + request);
}
}
2.2参数详解
2.2.1 new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2)
lengthFieldOffset = 0
lengthFieldLength = 2
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" |
+--------+----------------+ +--------+----------------+
假设请求报文的前两个字节为报文体的长度
并希望服务端能够收到完整的报文
则加入下面这个解码器
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2));
当加入这个解码器时会调用这个构造方法
public LengthFieldBasedFrameDecoder(
int maxFrameLength,
int lengthFieldOffset, int lengthFieldLength) {
this(maxFrameLength, lengthFieldOffset, lengthFieldLength, 0, 0);
}
则实际的参数为
maxFrameLength:Integer.MAX_VALUE(整体报文的最大长度,包括报文头和报文体)
lengthFieldOffset:0(长度字段的起始索引)
lengthFieldLength :2(长度字段的长度)
lengthAdjustment : 0
initialBytesToStrip :0
1.组装请求体:
String request = "HELLO, WORLD";
//创建一个字节缓冲区
ByteBuf buffer = Unpooled.buffer();
//写入两个字节的报文体长度,长度为12
buffer.writeShort(request.length());
//写入报文体
buffer.writeBytes(request.getBytes());
//发送
ch.writeAndFlush(buffer);
实际请求体为:
信息: [id: 0x87221f3a, L:/127.0.0.1:52524 - R:/127.0.0.1:8023] WRITE: 14B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+------------------------