Netty粘包与拆包解决方案(三)

这里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()));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sunshineAndAlways

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值