netty 粘包问题处理

来源:互联网 发布:linux内核优化 编辑:程序博客网 时间:2024/05/16 01:26

一般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));//设置定长解码器 长度设置为30public 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 0
原创粉丝点击