天天看點

Netty拆包粘包拆包粘包

拆包

拆包是指netty傳輸資料時候,會把一條資訊,拆成幾部分。比如:"hello "拆成"he" 和 "llo"等,

粘包

粘包是指将多條資訊連在一起 比如發送“hello”和"123",而受到的是 "hello123" 

解決方案:

  1. 消息定長,比如固定傳100字元,不夠的用空格補充
  2. 在尾部添加特殊字元
  3. 發送資訊是發送資訊長度

下面我們來看一下定長怎麼用

用戶端

@Data
public class MyMessageProtocol {

    //定義一次發送包體長度
    private int len;
    //一次發送包體内容
    private byte[] content;
}

public class NettyClientHandler extends SimpleChannelInboundHandler<MyMessageProtocol> {
    /**
     * 當用戶端連接配接伺服器完成就會觸發該方法
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for(int i = 0; i< 10; i++) {
            String msg = "檢測拆包";
            //建立協定包對象
            MyMessageProtocol messageProtocol = new MyMessageProtocol();
            messageProtocol.setLen(msg.getBytes(CharsetUtil.UTF_8).length);
            messageProtocol.setContent(msg.getBytes(CharsetUtil.UTF_8));
            ctx.writeAndFlush(messageProtocol);
        }
    }
}

public class PackMessageEncoder extends MessageToByteEncoder<MyMessageProtocol> {
    @Override
    protected void encode(ChannelHandlerContext ctx, MyMessageProtocol msg, ByteBuf out) throws Exception {
        System.out.println("MyMessageEncoder encode 方法被調用");
        out.writeInt(msg.getLen());
        out.writeInt(msg.getLen());
        out.writeBytes(msg.getContent());
    }
}
           

服務端

public class PackNettyServerHandler extends SimpleChannelInboundHandler<MyMessageProtocol> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MyMessageProtocol msg) throws Exception {
        System.out.println("====服務端接收到消息如下====");
        System.out.println("長度=" + msg.getLen());
        System.out.println("内容=" + new String(msg.getContent(), CharsetUtil.UTF_8));
    }

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

public class PackMessageDecoder extends ByteToMessageDecoder {

    int length = 0;

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        //int類型長度4
        if(in.readableBytes() >= 4) {
            //擷取到長度
            if (length == 0){
                length = in.readInt();
            }
            if (in.readableBytes() < length) {
                System.out.println("目前可讀資料不夠,繼續等待。。");
                return;
            }
            byte[] content = new byte[length];
            if (in.readableBytes() >= length){
                in.readBytes(content);

                //封裝成MyMessageProtocol對象,傳遞到下一個handler業務處理
                MyMessageProtocol messageProtocol = new MyMessageProtocol();
                messageProtocol.setLen(length);
                messageProtocol.setContent(content);
                //交給下個handler處理
                out.add(messageProtocol);
            }
            length = 0;
        }
    }
}