这里LengthFieldBasedFrameDecoder 与 LengthFieldPrepender 需要配合使用,其实本质上来讲,一个是解码,一个是编码。它们处理粘包的主要思想是在生成的数据包中添加一个长度字段,用户记录当前数据包的长度。LengthFieldBaedFrameDecoder会按照参数指定的包长度偏移量数据对接收的数据进行解码,从而得到目标消息体的数据,而LengthFieldPrepender则会在响应的数据前面添加指定的字节数据,这个字节数据中保存了当前消息的整体字节数据长度;
LengthFieldBasedFrameDecoder解码器的具体参数如下:
maxFrameLength:指定了每个包所能传递的最大数据包大小;
lengthFieldOffset:指定了长度字段在字节码中的偏移量;
lengthFieldLength:指定了长度字段所占用的字节长度;
LengthAdjustment:对一些不仅包含消息和消息体的数据进行消息头的长度调整,这样就可以只得到消息体的数据,这里LengthAdjustment指定的是消息头的长度;
initialBytesToStrip:对于长度字段在消息头中间的情况,可以通过initalBytesToStrip忽略掉消息头以及长度字段占用的字节。
//Server端
public class EchoServer {
public void bind(int port) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/**
* 这里将LengthFieldBasedFrameDecoder添加到pipeline的首位,因为其需要对接收到数据
* 进行长度字段解码,这里也会对数据进行粘包和拆包处理
* maxFrameLength:指定了每个包所能传递的最大数据包大小;
* lengthFieldOffset:指定了长度字段在字节码中的偏移量;
* lengthFieldLength:指定了长度字段所占用的字节长度;
* lengthAdjustment:对一些不仅包含有消息和消息体的数据进行消息头的长度的调整,这样就可以只得到
* 消息体的数据,这里的lengthAdjustment指定的就是消息头的长度;
*
*/
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 2, 0, 2));
//LengthFieldPrepender 是一个编码器,主要是在响应字节数据前面添加字节长度字段
pipeline.addLast(new LengthFieldPrepender(2));
//对经过粘包和拆包处理之后的数据进行json反序列化,从而得到user对象
pipeline.addLast(new JsonDecoder());
//对响应数据进行编码,主要是将user对象序列化为json
pipeline.addLast(new JsonEncoder());
//添加handler处理数据
pipeline.addLast(new EchoServerHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoServer().bind(7006);
}
}
//Client端
public class EchoClient {
public void connect(String host, int port) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0,
2, 0, 2));
pipeline.addLast(new LengthFieldPrepender(2));
pipeline.addLast(new JsonDecoder());
pipeline.addLast(new JsonEncoder());
pipeline.addLast(new EchoClientHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
channelFuture.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoClient().connect("127.0.0.1", 7006);
}
}
//Decoder 解码器
public class JsonDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out)
throws Exception {
byte[] bytes = new byte[msg.readableBytes()];
msg.readBytes(bytes);
User user = JSON.parseObject(new String(bytes, CharsetUtil.UTF_8), User.class);
out.add(user);
}
}
//Encoder 编码器
public class JsonEncoder extends MessageToByteEncoder<User> {
@Override
protected void encode(ChannelHandlerContext ctx, User msg, ByteBuf out)
throws Exception {
String userJson = JSON.toJSONString(msg);
ctx.writeAndFlush(Unpooled.wrappedBuffer(userJson.getBytes()));
}
}