黏包&拆包处理

黏包&拆包处理

网络传输的时候可能是到达一定的量比如4k会发送一次,可能会把多个消息一起推送过来 就是黏包。如果没有很好的协议解析这一段字节流,就会造成无法解析的情况。

解决方案可以是,按特定的格式去定义每一条消息,比如文件头+消息体,文件头里面描述消息体的大小,这样接受端就知道如何解析每一条消息了。

netty提供了很多frame的编解码器 本次解析LengthFieldBaseFrameDecoder

黏包出现 客户端发了10条数句,server端有时候 只打印一次,10条数据被融合了

public class ServerHandlerChai extends SimpleChannelInboundHandler<ByteBuf> {

    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    int count = 0;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        byte[] bytes = new byte[msg.readableBytes()];
        msg.readBytes(bytes);
        String content = new String(bytes , Charset.forName("UTF-8"));
        System.out.println("收到客户端的消息======"+content);
        System.out.println("收到客户端的消息 读取的次数======"+ (++count));

        ByteBuf byteBuf = Unpooled.copiedBuffer("这是服务器送给客户端的一段话" , Charset.forName("UTF-8"));
        ctx.writeAndFlush(byteBuf);

    }
}


public class SimpleClientHandlerChai extends SimpleChannelInboundHandler<ByteBuf> {

    int count = 0;

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf msg) {

        byte[] bytes = new byte[msg.readableBytes()];
        msg.readBytes(bytes);
        String content = new String(bytes , Charset.forName("UTF-8"));
        System.out.println(String.format("消息内容-> %s", content));
        System.out.println(String.format("消息次数-> %s", ++count));

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {

        for(int i=0;i<=10;i++){
            ByteBuf byteBuf = Unpooled.copiedBuffer("客户端来信=================     "+i+"/r/n" , Charset.forName("UTF-8"));
            ctx.writeAndFlush(byteBuf);
        }

    }
}

如何破解拆包黏包,自定义协议解析器

重点看下编码器和解码器

public class NettyServer {

    public static void main(String[] args) throws InterruptedException {

        NioEventLoopGroup bossGroupChai = new NioEventLoopGroup();
        NioEventLoopGroup workGroupChai = new NioEventLoopGroup();

        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap().
            group(bossGroupChai , workGroupChai).
            channel(NioServerSocketChannel.class).
            childHandler(new ChannelInitializer<SocketChannel>(){
                @Override
                protected void initChannel(SocketChannel channel) throws Exception {
                    ChannelPipeline pipeline = channel.pipeline();
                    pipeline.addLast("decoder" , new DeppyuDecoder());
                    pipeline.addLast("encoder" , new DeppyuEncoder());
                    pipeline.addLast("serverHandler", new ServerHandler());
                }
            });

            ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(5566)).sync();
            channelFuture.channel().closeFuture().sync();
        }finally {
            bossGroupChai.shutdownGracefully();
            workGroupChai.shutdownGracefully();
        }

    }

}

public class NettyClient {

    public static void main(String[] args) throws InterruptedException {

        EventLoopGroup eventGroup = new NioEventLoopGroup();

        try{
            Bootstrap bootstrap = new Bootstrap().group(eventGroup).
            channel(NioSocketChannel.class).
            handler(new ChannelInitializer<SocketChannel>(){
                @Override
                protected void initChannel(SocketChannel channel) throws Exception {
                    ChannelPipeline pipeline = channel.pipeline();
                    pipeline.addLast("decoder" , new DeppyuDecoder());
                    pipeline.addLast("encoder" , new DeppyuEncoder());
                    pipeline.addLast("clientHandler", new ClientHandler());
                }
            });

            ChannelFuture channelFuture = bootstrap.connect("localhost",5566).sync();
            channelFuture.channel().closeFuture().sync();
        }finally {
            eventGroup.shutdownGracefully();
        }

    }

}

public class ServerHandler extends SimpleChannelInboundHandler<DeppyuProtocol> {

    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DeppyuProtocol msg) throws Exception {
        String content = new String(msg.getBytes());
        System.out.println(String.format("收到服务器的消息内容-> %s", content));

        String answer = "做个好人吧";
        byte[] contentBytes = answer.getBytes();
        int length = contentBytes.length;

        DeppyuProtocol deppyu = new DeppyuProtocol();
        deppyu.setLength(length);
        deppyu.setBytes(contentBytes);

        ctx.writeAndFlush(deppyu);

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(String.format("[channelActive]-> [%s] 上线", channel.remoteAddress()));
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
//        System.out.println(String.format("[channelInactive]-> [%s] 下线", channel.remoteAddress()));
        channelGroup.writeAndFlush(String.format("[channelInactive]-> [%s] 下线 \n", channel.remoteAddress()));
        super.channelInactive(ctx);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        if(channelGroup.size()>=1){
            channelGroup.writeAndFlush(String.format("[handlerAdded]-> [%s] 加入 \n", channel.remoteAddress()));
        }
        channelGroup.add(channel);
        super.handlerAdded(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channelGroup.writeAndFlush(String.format("[服务端]-> [%s] 断开连接 \n", channel.remoteAddress()));
        super.handlerRemoved(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        ctx.close();
    }

}

public class ClientHandler extends SimpleChannelInboundHandler<DeppyuProtocol> {

    int count = 0;

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, DeppyuProtocol msg) {

        String content = new String(msg.getBytes());
        System.out.println(String.format("收到服务器的消息内容-> %s", content));

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {

        for(int i=0;i<=10;i++){
            String content = "客户端来信==="+i;
            int length = content.getBytes().length;
            byte[] bytes = content.getBytes();

            DeppyuProtocol deppyu = new DeppyuProtocol();
            deppyu.setLength(length);
            deppyu.setBytes(bytes);

            ctx.writeAndFlush(deppyu);
        }

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("异常======"+cause.getMessage());
        super.exceptionCaught(ctx, cause);
    }

}

public class DeppyuEncoder extends MessageToByteEncoder<DeppyuProtocol> {

    @Override
    public void encode(ChannelHandlerContext ctx, DeppyuProtocol msg, ByteBuf out){
        System.out.println("encode===========");
        out.writeInt(msg.getLength());
        out.writeBytes(msg.getBytes());
    }

}

public class DeppyuDecoder extends ReplayingDecoder<Void> {

    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out){
        System.out.println("decode===========");

        int length = in.readInt();
        byte[] content = new byte[length];
        in.readBytes(content);

        DeppyuProtocol deppyu = new DeppyuProtocol();
        deppyu.setLength(length);
        deppyu.setBytes(content);

        out.add(deppyu);
    }


}

关于replayingDecoer为啥可以认为数据全部到齐,具体的可以看链接的文章

大体和它的javadoc描述类似,设置mark点,如果数据没到齐 抛异常,后续再处理

ReplayingDecoder传递一个专门的ByteBuf实现,当缓冲区中没有足够的数据时,这个实现会抛出某种类型的错误。在上面的IntegerHeaderFrameDecoder中,您只是假设在调用buf.readInt()时,缓冲区中有4个或更多字节。如果缓冲区中确实有4个字节,它将像您期望的那样返回整数报头。否则,将引发错误并将控制返回到ReplayingDecoder。如果ReplayingDecoder捕捉到错误,那么它会将缓冲区的readerIndex倒回“初始”位置(即缓冲区的开始),并在缓冲区接收到更多数据时再次调用decode(..)方法。
请注意,ReplayingDecoder总是抛出相同的缓存错误实例,以避免每次抛出时创建新错误并填充其堆栈跟踪的开销。

【第23篇】Netty的ReplayingDecoder源码分析与特性解读 - 简书

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值