zoukankan      html  css  js  c++  java
  • netty 粘包问题处理

    netty 粘包问题处理

    key words: netty 粘包 解包 半包 TCP

    一般TCP粘包/拆包解决办法

    1. 定长消息,例如每个报文长度固定,不够补空格
    2. 使用回车换行符分割,在包尾加上分割符,例如Ftp协议
    3. 消息分割,头为长度(消息总长度或消息体长度),通常头用一个int32表示
    4. 复杂的应用层协议

    netty的几种解决方案

    特殊分隔符解码器:DelimiterBasedFrameDecoder

    客户端发送消息

    String message = "netty is a nio server framework &"
    				+"which enables quick and easy development &"
    				+"of net applications such as protocol &"
    				+"servers and clients!";
    

    服务端添加解码器:DelimiterBasedFrameDecoder

    ByteBuf delimiter = Unpooled.copiedBuffer("&".getBytes());
    ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,delimiter));
    //1024表示单条消息的最大长度,解码器在查找分隔符的时候,达到该长度还没找到的话会抛异常
    ch.pipeline().addLast(new StringDecoder());
    ....
    ch.pipeline().addLast(new StringEncoder());
    

    打印输出:

    接收消息:[netty is a nio server framework ]
    接收消息:[which enables quick and easy development ]
    接收消息:[of net applications such as protocol]
    接收消息:[servers and clients!]
    

    参数解释:

    public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf delimiter) {
        this(maxFrameLength, true, delimiter);
    }
    maxFrameLength:解码的帧的最大长度
    
    stripDelimiter:解码时是否去掉分隔符
    
    failFast:为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异常
    
    delimiter:分隔符
    

    定长解码器:FixedLengthFrameDecoder

    参数说明:

    • frameLength:帧的固定长度

    服务端

    ch.pipeline().addLast(new FixedLengthFrameDecoder(30));//设置定长解码器 长度设置为30
    
    
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        System.out.println("接收客户端msg:["+msg+"]");
        ByteBuf echo=Unpooled.copiedBuffer(MESSAGE.getBytes());
        ctx.writeAndFlush(echo);
    }
    

    客户端

    ch.pipeline().addLast(new FixedLengthFrameDecoder(30));//设置定长解码器
    

    基于包头不固定长度的解码器:LengthFieldBasedFrameDecoder

    参数说明

    • maxFrameLength:解码的帧的最大长度
    • lengthFieldOffset:长度属性的起始位(偏移位),包中存放有整个大数据包长度的字节,这段字节的其实位置
    • lengthFieldLength:长度属性的长度,即存放整个大数据包长度的字节所占的长度
    • lengthAdjustmen:长度调节值,在总长被定义为包含包头长度时,修正信息长度。
    • initialBytesToStrip:跳过的字节数,根据需要我们跳过lengthFieldLength个字节,以便接收端直接接受到不含“长度属性”的内容
    • failFast :为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异常

    备注:如果长度解析失误,(过大,就直接丢弃这个包;过小,1、netty不抛出异常;2、校验通不过)

    源码:
    int frameLengthInt = (int) frameLength;
    if (in.readableBytes() < frameLengthInt) {
    	return null;
    }
    

    封包时配合使用LengthFieldPrepender,很容易加上包长度

    包头添加总包长度字节:LengthFieldPrepender

    在发布时,自动在帧的头部加上长度
    参数说明:

    • lengthFieldLength:长度属性的字节长度

    • lengthIncludesLengthFieldLength:false,长度字节不算在总长度中,true,算到总长度中

      应用:
      pipeline.addLast("frameEncode", new LengthFieldPrepender(4, false));

    官方说明:

    编码类,自动将
    +----------------+	
    | "HELLO, WORLD" |	
    +----------------+
    
    格式的数据转换成如下格式的数据,	
    +--------+----------------+	
    + 0x000C | "HELLO, WORLD" |	
    +--------+----------------+
    
    如果lengthIncludesLengthFieldLength设置为true,则编码为(多了两个字节)
    +--------+----------------+	
    + 0x000E | "HELLO, WORLD" |	
    +--------+----------------+
    

    备注

    当时解决问题和记录时,是查阅了官网和几篇博客,如果里面内容有copy的地方,请留言url,我会把你的文章引用放到顶上去

  • 相关阅读:
    vue父子组件传值的方式
    定时任务写法
    仅仅为笔记
    consul剔除某个服务
    mybatis批量查询
    一次eureka的事故
    feign的工作原理
    JVM优化
    threadlocal应用
    秋招总结
  • 原文地址:https://www.cnblogs.com/sloong/p/5047743.html
Copyright © 2011-2022 走看看