版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
DelimiterBasedFrameDecoder
这是一个基于分隔符的解码器,并且可以自定义分隔符
第一个参数是能够接收数据的最大长度,后面的可变参数是分隔符,可以传递多个。
DelimiterBasedFrameDecoder delimiterBasedFrameDecoder =
new DelimiterBasedFrameDecoder(10, Unpooled.wrappedBuffer(new byte[]{'\n'}),Unpooled.wrappedBuffer(new byte[]{'\r','\n'}));
需要注意的是如果分隔符是\n
和\r\n
那么会直接使用LineBasedFrameDecoder
解码器来解码,不过传入的分隔符对象是很严格定义的
构造器方法,会判断传递过来的分隔符是不是\n
和\r\n
,如果是的话会创建一个LineBasedFrameDecoder
解码器,并赋值给lineBasedDecoder,会面在解码的时候会判断时候已经创建了解码器,如果创建了的话会直接用此解码器
public DelimiterBasedFrameDecoder(
int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters) {
validateMaxFrameLength(maxFrameLength);
if (delimiters == null) {
throw new NullPointerException("delimiters");
}
if (delimiters.length == 0) {
throw new IllegalArgumentException("empty delimiters");
}
if (isLineBased(delimiters) && !isSubclass()) {
lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
this.delimiters = null;
} else {
this.delimiters = new ByteBuf[delimiters.length];
for (int i = 0; i < delimiters.length; i ++) {
ByteBuf d = delimiters[i];
validateDelimiter(d);
this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes());
}
lineBasedDecoder = null;
}
this.maxFrameLength = maxFrameLength;
this.stripDelimiter = stripDelimiter;
this.failFast = failFast;
}
解码方法
@Override
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
Object decoded = decode(ctx, in);
if (decoded != null) {
out.add(decoded);
}
}
真正的解码方法,在调用过寻找分隔符的索引的方法后会出现4种情况,分情况会做不同的处理。
- 没有找到分隔符,超过最大长度,直接跳过本次要读取的数据(跳过到分隔符之前的数据),抛出异常
- 没有找到分隔符,超过最大长度,直接跳过整个buffer中的数据,设置开启丢弃模式,根据failFast属性判断是否马上抛出异常
- 找到了分隔符,处于丢弃模式,设置关闭丢弃模式,跳过本次要读取的数据(跳过到分隔符之前的数据),设置已经丢弃的字节长度为0,根据failFast属性判断是否马上抛出异常
- 没有找到分隔符,没有处于丢弃模式,累加丢弃的直接,丢弃整个buffer中的数据
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
//首先判断行解码器是否被实例化了 被实例化了就使用行处理器
if (lineBasedDecoder != null) {
return lineBasedDecoder.decode(ctx, buffer);
}
// 尝试所有分隔符并选择产生最短帧的分隔符。
int minFrameLength = Integer.MAX_VALUE;
ByteBuf minDelim = null;
for (ByteBuf delim: delimiters) {
//找到最小分隔符的位置
int frameLength = indexOf(buffer, delim);
if (frameLength >= 0 && frameLength < minFrameLength) {
//本次有效数据长度
minFrameLength = frameLength;
minDelim = delim;
}
}
//如果找到了
if (minDelim != null) {
//分隔符长度
int minDelimLength = minDelim.capacity();
ByteBuf frame;
//如果处于丢弃模式
if (discardingTooLongFrame) {
// We've just finished discarding a very large frame.
// Go back to the initial state.
//设置为非丢弃模式
discardingTooLongFrame = false;
//跳过本次要截取的数据
buffer.skipBytes(minFrameLength + minDelimLength);
//设置丢弃的长度为0
int tooLongFrameLength = this.tooLongFrameLength;
this.tooLongFrameLength = 0;
if (!failFast) {
fail(tooLongFrameLength);
}
return null;
}
//本次要截取的数据大于最大能截取的长度
if (minFrameLength > maxFrameLength) {
//丢弃本次可读数据加分割符
buffer.skipBytes(minFrameLength + minDelimLength);
fail(minFrameLength);
return null;
}
//有效数据是否要截取分隔符
if (stripDelimiter) {
frame = buffer.readRetainedSlice(minFrameLength);
buffer.skipBytes(minDelimLength);
} else {
frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);
}
return frame;
} else {
//如果没有找到分割符
//判断是否处于非丢弃模式
if (!discardingTooLongFrame) {
//如果本次可读取的数据长度大于最大可读取长度
if (buffer.readableBytes() > maxFrameLength) {
// 丢弃缓冲区的内容,直到找到分隔符为止。
//丢弃的长度等于本次可读的长度
tooLongFrameLength = buffer.readableBytes();
//丢弃
buffer.skipBytes(buffer.readableBytes());
//设置丢弃模式为true
discardingTooLongFrame = true;
if (failFast) {
fail(tooLongFrameLength);
}
}
} else {
//仍然丢弃缓冲区,因为没有找到分隔符。
tooLongFrameLength += buffer.readableBytes();
buffer.skipBytes(buffer.readableBytes());
}
return null;
}
}
找到分隔符的索引的方法
private static int indexOf(ByteBuf haystack, ByteBuf needle) {
//外层遍历需要读取的数据ByteBuf
for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) {
int haystackIndex = i;
int needleIndex;
//内层循环遍历分隔符的ByteBuf
for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) {
if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) {
break;
} else {
haystackIndex ++;
//这个if判断需要读取的数据是否已经达到了写指针的位置
//如果到了而且分隔符还没有遍历完的话就会直接返回-1
//防止读取数据的ByteBuf的索引超过写指针
if (haystackIndex == haystack.writerIndex() &&
needleIndex != needle.capacity() - 1) {
return -1;
}
}
}
//代码执行到这说明找到了分隔符
//这里是判断分隔符是否已经遍历完了
//如果遍历完就会把需要读取的数据的索引返回出去,这里把分隔符的索引减去了
if (needleIndex == needle.capacity()) {
// Found the needle from the haystack!
return i - haystack.readerIndex();
}
}
return -1;
}
LengthFieldBasedFrameDecoder
这是一个基于长度的解码器,这种解码器会把数据头的数值当作需要读取的数据的长度。
所以会规定数据头存储数据长度的数值的长度,同时在传输数据时会见数据的长度放在数据的头。
LengthFieldBasedFrameDecoder的一些参数
public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {
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;