说明
io.netty.handler.codec.DelimiterBasedFrameDecoder是ByteToMessageDecoder的一个实现类,用一个或多个分割符拆分接收到的 ByteBuf。这个主要用于解析分隔符在帧的末尾的情况。注意,如果分割符在帧的开头,那么解析出来的帧的长度是0,所以要用在末尾的分隔符。
在构造DelimiterBasedFrameDecoder实例的时候,用到了下面几个参数,用来控制拆分行为:
- maxFrameLength:拆分的帧最多有多少字节。
- stripDelimiter:拆分后的帧是否去掉分隔符,默认值为true。
- failFast:默认值为true。当为true的时候,如果解码器发现帧的长度大于maxFrameLength,就会抛出 TooLongFrameException,而不管是否真正读取了整个帧。如果值为false,实际读取的字节数大于maxFrameLength时,才会抛出TooLongFrameException。
- delimiters:多个分割符。
- delimiter:单个分割符。
代码示例
分割符在报文的结尾,正常解析
本示例验证的场景:
- 客户端发送了一个12字节的报文给服务端。报文开头的字节内容是0x68,结尾的字节内容是0x16。
- 服务端用了DelimiterBasedFrameDecoder来基于分隔符解析帧。解析的时候设置帧的最大字节数是12,去掉分隔符,分隔符是0x16。解析完成后,将去掉分隔符的帧传送给后面的ChannelHandler。
服务端代码片段
package com.thb.power.server;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/**
* 服务端的主函数
* @author thb
*
*/
public class MainStation {
private static final Logger logger = LogManager.getLogger();
static final int PORT = Integer.parseInt(System.getProperty("port", "22335"));
public static void main(String[] args) throws Exception {
logger.traceEntry();
// 配置服务器
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new MainStationInitializer());
// 启动服务端
ChannelFuture f = b.bind(PORT).sync();
// 等待直到server socket关闭
f.channel().closeFuture().sync();
} finally {
// 关闭所有event loops以便终止所有的线程
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
logger.traceExit();
}
}
package com.thb.power.server;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.thb.power.server.register.ServerRegisterRequestHandler;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
public class MainStationInitializer extends ChannelInitializer<SocketChannel> {
private static final Logger logger = LogManager.getLogger();
@Override
public void initChannel(SocketChannel ch) throws Exception {
logger.traceEntry();
ChannelPipeline p = ch.pipeline();
p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new DelimiterBasedFrameDecoder(12, true, Unpooled.wrappedBuffer(new byte[] {0x16})));
p.addLast(new ServerRegisterRequestHandler());
logger.traceExit();
}
}
package com.thb.power.server.register;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ServerRegisterRequestHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LogManager.getLogger();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
logger.traceEntry();
ByteBuf m = (ByteBuf)msg;
logger.info("readableBytes: " + m.readableBytes());
logger.traceExit();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
启动服务端
启动客户端,并向服务端发送包含12个字节的报文
观察服务端的输出
从服务端的输出可以看到,ServerRegisterRequestHandler收到了11个字节的数据,这是正确的。因为整个帧12个字节,DelimiterBasedFrameDecoder在解析的时候,我们通过参数控制,去掉了1个字节的分隔符。