Netty 中的拆包器
-
固定长度的拆包器 FixedLengthFrameDecoder
-
行拆包器 LineBasedFrameDecoder
-
分隔符拆包器 DelimiterBasedFrameDecoder
-
基于数据包长度的拆包器 LengthFieldBasedFrameDecoder
前3个比较好理解 按固定长度 或是分隔符进行切割 2和3在源码流程上类似 lineBase是固定模式在定位\n的时候判断前面是否有\r
LengthFieldBasedFrameDecoder 重要的参数有4个 抄自网络
- lengthFieldOffset - 定义长度域位于发送的字节数组中的下标。换句话说:发送的字节数组中下标为${lengthFieldOffset}的地方是长度域的开始地方
- lengthFieldLength - 用于描述定义的长度域的长度。换句话说:发送字节数组bytes时, 字节数组bytes[lengthFieldOffset, lengthFieldOffset+lengthFieldLength]域对应于的定义长度域部分,unsupported lengthFieldLength: 0 (expected: 1, 2, 3, 4, or 8)
- lengthAdjustment - 满足公式:
公式: 数据包值 = 长度域 + lengthFieldOffset+ lengthFieldLength + lengthAdjustment
- initialBytesToStrip - 接收到的发送数据包,去除前initialBytesToStrip位
对于理解这几个参数 假设实际内容长度 放在最前面的情况 消息头跟在后面
bytePuf.writeShort(length);//内容升序
bytePuf.writeByte(1);
bytePuf.writeInt(-21415431);//4
bytePuf.writeShort(11);//
bytePuf.writeShort(22);//
new LengthFieldBasedFrameDecoder(16384, 0, 2, 9, 0)
消息头容量 9= 1+4+2+2 最后一个0 相当于在我们在取的解码内容的时候 从第0个开始
依次会读到实际内容长度 然后是后面9位消息头 再实际内容
如果最后一个initialBytesToStrip 0 改为11 略过前面11位长度+消息头 可以直接得到消息体内容
一般使用场景需要验证消息头
lengthAdjustment 也就是常观 长度(表示内容长度)开头的情况下 值可以理解自定义除去长度 后面定义消息头的容量
另一种情况 长度域如果表示的长度是总长度 这种定义不太好理解 应该尽量避免这种方法去定义
* BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes)
* +--------+----------------+ +--------+----------------+
* | Length | Actual Content |----->| Length | Actual Content |
* | 14 | "HELLO, WORLD" | | 14 | "HELLO, WORLD" |
* +--------+----------------+ +--------+----------------+
长度域表示的长度是总长度 也就是header+body的总长度。参数如下:
lengthFieldOffset=0:开始的2个字节就是长度域,所以不需要长度域偏移。
lengthFieldLength=2:长度域2个字节。
lengthAdjustment=-2:因为长度域为总长度,所以我们需要修正数据长度,也就是减去2。
initialBytesToStrip=0:我们发现接收的数据没有长度域的数据,所以要跳过长度域的2个字节。
————————————————
总结一下这种组织的问题 一般在定义协议的时候 会自定义一个消息头 比如dubbo 固定魔数开头
还是以上面 9位消息头来说 9位消息头+长度+容量
还是 衣旧上面定义9位消息头
bytePuf.writeByte(1);
bytePuf.writeInt(-21415431);//4
bytePuf.writeShort(11);//
bytePuf.writeShort(22);//
然后 从第9位取得容量长度 new LengthFieldBasedFrameDecoder(16384, 9, 2, 0, 0)
用LengthFieldBasedFrameDecoder定义dubbo消息头
{
ByteBuf bytePuf = ByteBufAllocator.DEFAULT.buffer();
int length = json.getBytes().length;
bytePuf.writeShort(0xdabb);//MAGIC
bytePuf.writeByte(0x80);//FLAG_REQUEST
bytePuf.writeByte(20);//status
bytePuf.writeLong(System.currentTimeMillis());//invoke id
bytePuf.writeInt(length);//body length:消息体 body 长度
bytePuf.writeBytes(json.getBytes());
cannel.writeAndFlush(bytePuf);
}
LengthFieldBasedFrameDecoder(16384, 12, 4, 0, 0)
{
short magic = buf.readShort();//MAGIC
byte flag = buf.readByte();
byte status = buf.readByte();
long requestId = buf.readLong();
int length = buf.readInt();
}
实际LengthFieldBasedFrameDecoder 运用中出现以下异常
io.netty.handler.codec.TooLongFrameException: Adjusted frame length exceeds 16384: 20033 - discarded
实际可能不是包的长度大于设置最长长度16384
比如 协议内容的长度稍大于的定义的长度
多半是非法请求了 可以跟踪一下限制消息来源ip解决
所以简单的长度+内容 其实还是比较不安全的
还是稍微的定义一个协议头 比如什么开头 加个自己的版本号之类