前面说到,LengthFieldBasedFrameDecoder
类是我们最常用的一个粘包拆包工具,能帮我们解决95%以上的粘包拆包问题。
LengthFieldBasedFrameDecoder
类能够解决的最复杂的数据包结构类似如下:
从上图可以看出,这种协议类似“包头1 + 包体长度 + 包头2 + 包体”这种结构。
这种协议结构已经很复杂了,但现实往往不尽如人意,比如有如下形式的数据协议:
这种数据协议结构类似于“包头 + 包体长度 + 包体 +包尾”。
很明显,这种带“包尾”的协议结构,就是LengthFieldBasedFrameDecoder
类不能解决的。
因此,我们必须自定义这种协议的LengthFieldBasedFrameDecoder
类。
我们通过对LengthFieldBasedFrameDecoder
类进行预研,发现我们自定义的LengthFieldBasedFrameDecoder
类不能够通过继承LengthFieldBasedFrameDecoder
类来解决问题,只能把LengthFieldBasedFrameDecoder
类的代码拷贝过来修改。
开始的代码如下:
public class CashboxDataLengthFieldBasedFrameDecoder extends ByteToMessageDecoder {
private static final int TAIL_LENGTH = 7;
private final ByteOrder byteOrder;
private final int maxFrameLength;
private final int lengthFieldOffset;
private final int lengthFieldLength;
private final int lengthFieldEndOffset;
private final int lengthAdjustment;
private final int initialBytesToStrip;
private final boolean failFast;
private boolean discardingTooLongFrame;
private long tooLongFrameLength;
private long bytesToDiscard;
所有的粘包拆包解码器类,都需要继承ByteToMessageDecoder
类。
再往下,就是我们自定义的包尾长度-private static final int TAIL_LENGTH = 7;
,后面都是拷贝LengthFieldBasedFrameDecoder
类的代码。
最后的修改在decode
方法里:
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
if (discardingTooLongFrame) {
discardingTooLongFrame(in);
}
if (in.readableBytes() < lengthFieldEndOffset) {
return null;
}
int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;
long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);
if (frameLength < 0) {
failOnNegativeLengthField(in, frameLength, lengthFieldEndOffset);
}
/**
* 增加的代码,加上尾部的长度,作为内容的长度。
*/
frameLength += TAIL_LENGTH;
frameLength += lengthAdjustment + lengthFieldEndOffset;
可以看到,仅有的改动就在内容长度-frameLength
上,将这个长度再加上包尾的长度,就可以了。
后面的代码,就都是考虑原LengthFieldBasedFrameDecoder
类的代码了,在此就不再多说了。