定长协议
1 FixedLengthFrameDecoder简介
FixedLengthFrameDecoder采用的是定长协议:即把固定的长度的字节数当做一个完整的消息
例如,我们规定每3个字节,表示一个有效报文,如果我们分4次总共发送以下9个字节:
+---+----+------+----+
| A | BC | DEFG | HI |
+---+----+------+----+
那么通过FixedLengthFrameDecoder解码后,实际上只会解析出来3个有效报文
+-----+-----+-----+
| ABC | DEF | GHI |
+-----+-----+-----+
FixedLengthFrameDecodert提供了以下构造方法
public FixedLengthFrameDecoder(int frameLength) {
if (frameLength <= 0) {
throw new IllegalArgumentException(
"frameLength must be a positive integer: " + frameLength);
}
this.frameLength = frameLength;
}
其中:frameLength
就是我们指定的长度。
需要注意的是FixedLengthFrameDecoder并没有提供一个对应的编码器,因为接收方只需要根据字节数进行判断即可,发送方无需编码
2 FixedLengthFrameDecoder使用案例
server端:FixedLengthFrameDecoderServer
public class FixedLengthFrameDecoderServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new FixedLengthFrameDecoder(3));
// 自定义这个ChannelInboundHandler打印拆包后的结果
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
ByteBuf packet = (ByteBuf) msg;
System.out.println(
new Date().toLocaleString() + ":" + packet.toString(Charset.defaultCharset()));
}
}
});
}
});
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(8080).sync(); // (7)
System.out.println("FixedLengthFrameDecoderServer Started on 8080...");
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
Client端:FixedLengthFrameDecoderClient
public class FixedLengthFrameDecoderClient {
public static void main(String[] args) throws Exception {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); // (1)
b.group(workerGroup); // (2)
b.channel(NioSocketChannel.class); // (3)
b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
//在于server建立连接后,即发送请求报文
public void channelActive(ChannelHandlerContext ctx) {
ByteBuf A = Unpooled.buffer().writeBytes("A".getBytes());
ByteBuf BC = Unpooled.buffer().writeBytes("BC".getBytes());
ByteBuf DEFG = Unpooled.buffer().writeBytes("DEFG".getBytes());
ByteBuf HI = Unpooled.buffer().writeBytes("HI".getBytes());
ctx.writeAndFlush(A);
ctx.writeAndFlush(BC);
ctx.writeAndFlush(DEFG);
ctx.writeAndFlush(HI);
}
});
}
});
// Start the client.
ChannelFuture f = b.connect("127.0.0.1",8080).sync(); // (5)
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
}
先后运行server端与client端后,server端控制台输出
LineBasedFrameDecoderServer Started on 8080...
2018-9-8 15:20:20:ABC
2018-9-8 15:20:20:DEF
2018-9-8 15:20:20:GHI
可以看到FixedLengthFrameDecoder的确将请求的数据,按照每3个字节当做一个完整的请求报文。
通常情况下,很少有client与server交互时,直接使用定长协议,可能会造成浪费。例如你实际要发送的实际只有3个字节,但是定长协议设置的1024,那么可能你就要为这3个字节基础上,在加1021个空格,以便server端可以解析这个请求。在下一节我们将要介绍的LengthFieldBasedFrameDecoder,支持动态指定报文的长度。