netty 粘包问题处理

一般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" | 
+--------+----------------+
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值