LengthFieldBasedFrameDecoder使⽤⽰例
转载⾃:blog.csdn/z69183787/article/details/52980699
blog.163/linfenliang@126/blog/static/127857195201210821145721/
LengthFieldBasedFrameDecoder是Netty中⼀个很重要的解码器,因为相⽐于其他的普通的解码器,这个解码器⽤的场景更多。
定义⼀个这样的协议类:
public class CustomMsg {
//类型系统编号 0xAB 表⽰A系统,0xBC 表⽰B系统
private byte type;
//信息标志 0xAB 表⽰⼼跳包 0xBC 表⽰超时包 0xCD 业务信息包
private byte flag;
//主题信息的长度
private int length;
//主题信息
private String body;
……
}
规定两个系统通过Netty去发送这样的⼀个格式的信息,CustomMsg中包含这样的⼏类信息:type表⽰发送端的系统类型;flag表⽰发送信息的类型,是业务数据,还是⼼跳包数据;length表⽰主题body的长度;body表⽰主题信息。
LengthFieldBasedFrameDecoder的构造⽅法中有⼏个重要的参数:
maxFrameLength:解码时,处理每个帧数据的最⼤长度
lengthFieldOffset :存放帧数据的长度数据的起始位(偏移位)
lengthFieldLength:长度属性的长度,即存放整个⼤数据包长度的字节所占的长度
lengthAdjustmen:长度调节值,在总长被定义为包含包头长度时,修正信息长度。
initialBytesToStrip:跳过的字节数,根据需要跳过lengthFieldLength个字节,以便接收端直接接受到不含“长度属性”的内容
failFast :为true,当frame长度超过maxFrameLength时⽴即报TooLongFrameException异常,为false,读取完整个帧再报异常
简单举个例⼦说下含义,例如对于数据:
BEFORE DECODE (14 bytes)
+---------+----------------+
| Length | Actual Content|
| 0x000C | "HELLO, WORLD"|
+---------+----------------+
0x000C表⽰有效数据是12个字节,如下设置时:
decoder
lengthFieldOffset = 0
lengthFieldLength = 2
lengthAdjustment = 0
initialBytesToStrip = 0
解析完数据为:
| 0x000C | "HELLO, WORLD" |
此时数据格式不做任何改变(没有跳过任何字节)。
如下设置时:
lengthFieldOffset = 0
lengthFieldLength = 2
lengthAdjustment = 0
initialBytesToStrip = 2
解析完数据为:
| "HELLO, WORLD" |
但由于前两个(lengthFieldOffset = 0,lengthFieldLength = 2)字节是表⽰帧长度的字节,不计⼊数据,故真实的数据是除去了长度数据后剩下的12个字节。
例如对于数据:
BEFORE DECODE (14 bytes)
+---------+----------------+
| Length | Actual Content|
| 0x000E | "HELLO, WORLD"|
+---------+----------------+
此处定义的Length为0x000E,占了两个字节,表⽰的整个帧长度为14(2 + 12)个字节。如下设置时:
lengthFieldOffset = 0
lengthFieldLength = 2
lengthAdjustment = -2
initialBytesToStrip = 0
解析完数据为:
| 0x000E | "HELLO, WORLD" |
由于设置的lengthAdjustment = -2 (即 the length of the Length field),故修正的信息实际长度补2,即解码时往前推2个字节,解码后还是14个字节长度。更具体的还请⾃⾏百度。
现在⾃定义⼀个decoder,这个类继承LengthFieldBasedFrameDecoder,来解析刚才定义的CustomMsg:
public class CustomDecoder extends LengthFieldBasedFrameDecoder {
//判断传送客户端传送过来的数据是否按照协议传输,头部信息的⼤⼩应该是 byte+byte+int = 1+1+4 = 6
private static final int HEADER_SIZE = 6;
private byte type;
private byte flag;
private int length;
private String body;
public CustomDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,
int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength,
lengthAdjustment, initialBytesToStrip, failFast);
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
if (in == null) {
return null;
}
if (in.readableBytes() < HEADER_SIZE) {
throw new Exception("可读信息段⽐头部信息都⼩,你在逗我?");
}
//注意在读的过程中,readIndex的指针也在移动
type = in.readByte();
flag = in.readByte();
length = in.readInt();
if (in.readableBytes() < length) {
throw new Exception("body字段你告诉我长度是"+length+",但是真实情况是没有这么多,你⼜逗我?");
}
ByteBuf buf = in.readBytes(length);
byte[] req = new adableBytes()];
body = new String(req, "UTF-8");
CustomMsg customMsg = new CustomMsg(type,flag,length,body);
return customMsg;
}
}
头部信息的⼤⼩是6,因为byte是⼀个字节,int是四个字节,那么头部⼤⼩就是6个字节,构造函数的⼊参数据是:
maxFrameLength = 1024 * 1024
lengthFieldOffset = 2
lengthFieldLength = 4
lengthAdjustment = 0
initialBytesToStrip = 0
lengthFieldLength指的就是CustomMsg中length这个属性的⼤⼩,是int型,所以是4;lengthFieldOffset指的就是length字段的起始位置,因为前⾯有type和flag两个属性,且这两个属性都是byte,两个就是2字节,所以偏移量是2;lengthAdjustment指的是length这个属性的值,假如body长
度是40,有时候,有些⼈喜欢将length写成44,因为length本⾝还占有4个字节,这样就需要调整⼀下,那么就需要-4,我们这边没有这样做,所以写0就可以了。
⾃定义⼀个encoder来编码CustomMsg,代码如下:
public class CustomEncoder extends MessageToByteEncoder<CustomMsg> {
@Override
protected void encode(ChannelHandlerContext ctx, CustomMsg msg, ByteBuf out) throws Exception { if(null == msg){
throw new Exception("msg is null");
}
String body = Body();
byte[] bodyBytes = Bytes(Charset.forName("utf-8"));
out.Type());
out.Flag());
out.writeInt(bodyBytes.length);
out.writeBytes(bodyBytes);
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论