黏包&拆包處理

黏包&拆包處理

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總是拋出相同的緩存錯誤實例,以避免每次拋出時創建新錯誤並填充其堆棧跟蹤的開銷。

https://www.jianshu.com/p/9cc9ee3e7ddc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章