Netty网络编程五:Netty粘包拆包解码器源码分析与自定义解码器

Netty网络编程五:Netty粘包拆包解码器源码分析之自定义解码器

一:概述

由上一篇文章讲完了ByteToMessageDecoder,再来看其实现LineBasedFrameDecoder ,DelimiterBasedFrameDecoder,FixedLengthFrameDecoder发现只要实现decode()方法即可,并按照指定的分割条件进行字节流数据分割,然后将处理好的数据加入到out的集合中即可。

二:Netty解码器源码

1) 先看FixedLengthFrameDecoder解码器,这事一个固定长度的解码器

这个解码器源码比较简单
是继承ByteToMessageDecoder,实现抽象方法decode,
注意: 这个decode方法的第二个参数in ,就是ByteToMessageDecode中的缓冲累计器,即存放所有累计的缓冲数据 ,也就是

ByteBuf cumulation;
private Cumulator cumulator = MERGE_CUMULATOR;
@Override
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    // 1.解码数据
    Object decoded = decode(ctx, in);
    if (decoded != null) {
        // 3.通过 把解码出来的数据交给out集合中即可
        out.add(decoded);
    }
}

具体解码实现:

protected Object decode(
        @SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, ByteBuf in) throws Exception {
    // 2. 解码
    // 缓冲容器中的可读数据小于长度,说明数据不足        
    if (in.readableBytes() < frameLength) {
        return null;
    } else {
        //返回frameLengt长度的数据,并更新in缓冲中的readerIndex()索引位置
        return in.readRetainedSlice(frameLength);
    }
}

2) LineBasedFrameDecoder 解码器,就是安装"\n" 或"\r\n"进行分割的解码器,也就是回车换行分隔符

一般我们在使用这个解码器时,会先传一个长度的构造参数,表名最大解码长度,超过这个长度没解码处理一条数据,则抛出异常,这也是避免内存溢出

public LineBasedFrameDecoder(final int maxLength) {
    this(maxLength, true, false);
}

同样,看decode方法的内部实现

@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);
    }
}

也是同样的处理思路, 看下decode(ctx, in)的实现:

protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    // eol 表示
    final int eol = findEndOfLine(buffer);
    // discarding表示是否超过指定的maxLength,第一次进来为false
    if (!discarding) {
        if (eol >= 0) {
            final ByteBuf frame; // 要返回的解码后的对象
            final int length = eol - buffer.readerIndex(); // 这个要返回的对下的长度
            final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1; //分隔符长度, '\n' 还是'\r\n'

            if (length > maxLength) { // 超出长度,抛出异常
                buffer.readerIndex(eol + delimLength);
                fail(ctx, length);
                return null;
            }

            if (stripDelimiter) {// stripDelimiter表示解码时是否跳过分隔符,默认true
                frame = buffer.readRetainedSlice(length); // 读对象
                buffer.skipBytes(delimLength); //跳过分隔符
            } else {
                //不跳过分隔符情况
                frame = buffer.readRetainedSlice(length + delimLength);
            }

            return frame;// 返回解析处理的对象
        } else { // 可读数据超过maxLength 抛弃异常数据
            final int length = buffer.readableBytes();
            if (length > maxLength) {
                discardedBytes = length;
                buffer.readerIndex(buffer.writerIndex());
                discarding = true;
                offset = 0;
                if (failFast) {
                    fail(ctx, "over " + discardedBytes);
                }
            }
            return null;
        }
    } else { //丢弃数据
        if (eol >= 0) {
            final int length = discardedBytes + eol - buffer.readerIndex();
            final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
            buffer.readerIndex(eol + delimLength);
            discardedBytes = 0;
            discarding = false;
            if (!failFast) {
                fail(ctx, length);
            }
        } else {
            discardedBytes += buffer.readableBytes();
            buffer.readerIndex(buffer.writerIndex());
            // We skip everything in the buffer, we need to set the offset to 0 again.
            offset = 0;
        }
        return null;
    }
}

看下findEndOfLine(buffer);方法获取要解码数据的末尾的一条索引位置,及找到下一个分隔符位置

/**
 * 返回找到的行末尾的缓冲区中的索引。如果缓冲区中没有找到行尾,则返回-1。
 */
private int findEndOfLine(final ByteBuf buffer) {
    // 缓冲中带读取数据总长度
    int totalLength = buffer.readableBytes();
    // 遍历缓冲,找到'\n'符号的位置索引
    int i = buffer.forEachByte(buffer.readerIndex() + offset, totalLength - offset, ByteProcessor.FIND_LF);
    // 索引找找后,看是否这个索引位置前一位时'\r'(不同操作系统换行符区别),然后返回索引位置
    if (i >= 0) {
        offset = 0;
        if (i > 0 && buffer.getByte(i - 1) == '\r') {
            i--;
        }
    } else {
        offset = totalLength;
    }
    return i;
}

3) DelimiterBasedFrameDecoder指定分割符解码器

@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);
    }
}

查看decode(ctx, in)

protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    if (lineBasedDecoder != null) {// 指定为 LineBasedFrameDecoder解码器
        return lineBasedDecoder.decode(ctx, buffer);
    }
    // Try all delimiters and choose the delimiter which yields the shortest frame.
    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);

            int tooLongFrameLength = this.tooLongFrameLength;
            this.tooLongFrameLength = 0;
            if (!failFast) {
                fail(tooLongFrameLength);
            }
            return null;
        }

        if (minFrameLength > maxFrameLength) {
            // Discard read frame.
            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) {
                // Discard the content of the buffer until a delimiter is found.
                tooLongFrameLength = buffer.readableBytes();
                buffer.skipBytes(buffer.readableBytes());
                discardingTooLongFrame = true;
                if (failFast) {
                    fail(tooLongFrameLength);
                }
            }
        } else {
            // Still discarding the buffer since a delimiter is not found.
            tooLongFrameLength += buffer.readableBytes();
            buffer.skipBytes(buffer.readableBytes());
        }
        return null;
    }
}

综上,所有的解码器都是一致的, 即继承ByteToMessageDecoder接口,实现 decode(ChannelHandlerContext ctx, ByteBuf in, List out)方法, 最后把解码出来的对象存放在out集合中即可

三:自定义解码器

自定义一个解码器,也如同上面总结的那样,继承ByteToMessageDecoder接口,实现 decode(ChannelHandlerContext ctx, ByteBuf in, List out)方法即可.

文章中案例代码:https://download.csdn.net/download/qq_22871607/11072379

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值