DelimiterBasedFrameDecoder 基于自定义分隔符解码器,他能够按照我们自定义的特殊符号或者字符作为分隔符对接收到的消息进行分段解码,在服务端接收到信息进行解析的时候DelimiterBasedFrameDecoder的构造方法需要传递两个参数maxFrameLength和可变长参数delimiters,maxFrameLength用来限制对接收到的数据进行解码的时候一次解码的最大帧长度。如果在解码的过程中对传递的数据进行解码后大于maxLength,并且此时开启了快速失败机制,则会立即抛出异常信息。反之,如果在一次解码的过程中对传递的数据进行解码后小于于maxLength,那就正常的读取解码后的数据信息。可变长参数delimiters表示服务端解码的时候自己定义的分隔符(一个或者多个),如果只有一个那么就会按照这个分隔符对接收到的数据进行分割,如果为多个,那就会按照自定义的多个分隔符进行解码数据,如果最后一个分隔符后面仍然有数据(即不是以分隔符结尾),那么最后一个分隔符后面的数据皆不会被解码,实际上还是在缓冲区中,只是并未被读取出来。
public class DelimiterBasedFrameDecoderTestServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) {ByteBuf delimiter1 = Unpooled.copiedBuffer("%".getBytes());ByteBuf delimiter2 = Unpooled.copiedBuffer("$".getBytes());ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter1, delimiter2)).addLast(new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {if (msg instanceof ByteBuf) {ByteBuf packet = (ByteBuf) msg;System.out.println(packet.toString(Charset.defaultCharset()));}}});}});ChannelFuture f = b.bind(9000).sync();System.out.println("Started DelimiterBasedFrameDecoderTestServer...");f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}}
public class DelimiterBasedFrameDecoderTestClient {public static void main(String[] args) throws Exception {EventLoopGroup workerGroup = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(workerGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) {ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {public void channelActive(ChannelHandlerContext ctx) {ByteBuf byteBuf = Unpooled.copiedBuffer("he111%llo%world$w$orl$$dworld%ttt".getBytes());ctx.writeAndFlush(byteBuf);}});}});ChannelFuture f = b.connect("127.0.0.1", 9000).sync();System.out.println("Started DelimiterBasedFrameDecoderTestClient...");f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();}}}


netty提供的各种解码器统一都继承了ByteToMessageDecoder,ByteToMessageDecoder负责读取字节流并转换成其他消息,而ByteToMessageDecoder又继承了ChannelInboundHandlerAdapter,而ChannelInboundHandlerAdapter正是我们刚才在测试数据的时候在客户端重写了他的方法channelActive(),使管道生效并且数据写入缓冲区并发送数据。
@Overridepublic ChannelHandlerContext fireChannelRead(final Object msg) {//寻找下一个绑定的Handler并且调用ChannelRead方法invokeChannelRead(findContextInbound(), msg);return this;}
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);EventExecutor executor = next.executor();if (executor.inEventLoop()) {next.invokeChannelRead(m);} else {executor.execute(() -> next.invokeChannelRead(m));}}
private void invokeChannelRead(Object msg) {if (invokeHandler()) {try {((ChannelInboundHandler) handler()).channelRead(this, msg);} catch (Throwable t) {notifyHandlerException(t);}} else {fireChannelRead(msg);}}
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {try {while (in.isReadable()) {int outSize = out.size();//将out里的事件执行并且清空outif (outSize > 0) {fireChannelRead(ctx, out, outSize);out.clear();// 在继续解码之前,请检查是否已删除此处理程序// 如果已将其删除,则继续在缓冲区上操作是不安全的。if (ctx.isRemoved()) {break;}outSize = 0;}int oldInputLength = in.readableBytes();// 开始解析数据,如果解析出来数据,那么out的长度一定会改变decode(ctx, in, out);// 在继续循环之前,请检查是否已删除此处理程序。// 如果已将其删除,则继续在缓冲区上操作是不安全的。if (ctx.isRemoved()) {break;}// 如果没有解析出来数据if (outSize == out.size()) {if (oldInputLength == in.readableBytes()) {break;} else {continue;}}if (oldInputLength == in.readableBytes()) {throw new DecoderException(StringUtil.simpleClassName(getClass()) +".decode() did not read anything but decoded a message.");}if (isSingleDecode()) {break;}}} catch (DecoderException e) {throw e;} catch (Throwable cause) {throw new DecoderException(cause);}}
static void fireChannelRead(ChannelHandlerContext ctx, List<Object> msgs, int numElements) {if (msgs instanceof CodecOutputList) {fireChannelRead(ctx, (CodecOutputList) msgs, numElements);} else {for (int i = 0; i < numElements; i++) {ctx.fireChannelRead(msgs.get(i));}}}
@Overridepublic ChannelHandlerContext fireChannelRead(final Object msg) {//寻找下一个绑定的Handler并且调用ChannelRead方法invokeChannelRead(findContextInbound(), msg);return this;}
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);EventExecutor executor = next.executor();if (executor.inEventLoop()) {next.invokeChannelRead(m);} else {executor.execute(() -> next.invokeChannelRead(m));}}private void invokeChannelRead(Object msg) {if (invokeHandler()) {try {((ChannelInboundHandler) handler()).channelRead(this, msg);} catch (Throwable t) {notifyHandlerException(t);}} else {fireChannelRead(msg);}}

// ====================核心属性====================//自定义的分隔符数量,可以为1个或者多个private final ByteBuf[] delimiters;//每帧消息的最大长度private final int maxFrameLength;//解码消息时,是否丢弃分隔符private final boolean stripDelimiter;//是否开启快速失败机制(遇到错误时,是否立即抛出异常)private final boolean failFast;//是否正在丢弃一个帧的消息private boolean discardingTooLongFrame;//丢弃消息的总长度private int tooLongFrameLength;/** 仅在使用“ \ n”和“ \ r \ n”作为分隔符进行解码时设置. */private final LineBasedFrameDecoder lineBasedDecoder;
@Overrideprotected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {Object decoded = decode(ctx, in);if (decoded != null) {out.add(decoded);}}
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {//如果lineBasedDecoder不为空则用lineBasedDecoder解码器解析if (lineBasedDecoder != null) {return lineBasedDecoder.decode(ctx, buffer);}// 遍历所有定界符,然后选择产生最短帧的定界符。int minFrameLength = Integer.MAX_VALUE;ByteBuf minDelim = null;for (ByteBuf delim: delimiters) {//依次遍历每一个分隔符,找到产生的最短帧的长度和该分隔符delimint frameLength = indexOf(buffer, delim);if (frameLength >= 0 && frameLength < minFrameLength) {minFrameLength = frameLength;minDelim = delim;}}//如果找到了分隔符delimif (minDelim != null) {int minDelimLength = minDelim.capacity();ByteBuf frame;//如果找到分隔符delim 并且之前在丢弃模式中if (discardingTooLongFrame) {// 继续丢弃消息.// 回到初始状态.discardingTooLongFrame = false;buffer.skipBytes(minFrameLength + minDelimLength);int tooLongFrameLength = this.tooLongFrameLength;this.tooLongFrameLength = 0;if (!failFast) {fail(tooLongFrameLength);}return null;}//如果找到分隔符delim 并且之前不在丢弃模式中 判断是否超过每帧最大长度if (minFrameLength > maxFrameLength) {// Discard read frame.buffer.skipBytes(minFrameLength + minDelimLength);fail(minFrameLength);return null;}//如果找到分隔符delim 并且之前不在丢弃模式中 没有超过每帧最大长度 那么就是正常读取消息 正常的流程if (stripDelimiter) {frame = buffer.readRetainedSlice(minFrameLength);buffer.skipBytes(minDelimLength);} else {frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);}return frame;//如果没有找到分隔符delim} else {//如果没有找到分隔符delim 判断之前是否在丢弃模式中if (!discardingTooLongFrame) {//如果没有找到分隔符delim 并且之前不在丢弃模式中 那么就判断是否超过每帧最大长度if (buffer.readableBytes() > maxFrameLength) {// 丢弃缓冲区的内容,直到找到定界符.tooLongFrameLength = buffer.readableBytes();buffer.skipBytes(buffer.readableBytes());discardingTooLongFrame = true;// 抛出异常if (failFast) {fail(tooLongFrameLength);}}} else {//如果没有找到分隔符delim 并且之前在丢弃模式中// 由于找不到分隔符,因此仍在丢弃缓冲区.tooLongFrameLength += buffer.readableBytes();buffer.skipBytes(buffer.readableBytes());}return null;}}
文章转载自文一西路代码狗,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




