定長(zhǎng)協(xié)議
1 FixedLengthFrameDecoder簡(jiǎn)介
FixedLengthFrameDecoder采用的是定長(zhǎng)協(xié)議:即把固定的長(zhǎng)度的字節(jié)數(shù)當(dāng)做一個(gè)完整的消息
例如,我們規(guī)定每3個(gè)字節(jié),表示一個(gè)有效報(bào)文,如果我們分4次總共發(fā)送以下9個(gè)字節(jié):
+---+----+------+----+
| A | BC | DEFG | HI |
+---+----+------+----+
那么通過(guò)FixedLengthFrameDecoder解碼后,實(shí)際上只會(huì)解析出來(lái)3個(gè)有效報(bào)文
+-----+-----+-----+
| ABC | DEF | GHI |
+-----+-----+-----+
FixedLengthFrameDecodert提供了以下構(gòu)造方法
public FixedLengthFrameDecoder(int frameLength) {
if (frameLength <= 0) {
throw new IllegalArgumentException(
"frameLength must be a positive integer: " + frameLength);
}
this.frameLength = frameLength;
}
其中:frameLength就是我們指定的長(zhǎng)度。
需要注意的是FixedLengthFrameDecoder并沒(méi)有提供一個(gè)對(duì)應(yīng)的編碼器,因?yàn)榻邮辗街恍枰鶕?jù)字節(jié)數(shù)進(jìn)行判斷即可,發(fā)送方無(wú)需編碼
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));
// 自定義這個(gè)ChannelInboundHandler打印拆包后的結(jié)果
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建立連接后,即發(fā)送請(qǐng)求報(bào)文
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();
}
}
}
先后運(yùn)行server端與client端后,server端控制臺(tái)輸出
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的確將請(qǐng)求的數(shù)據(jù),按照每3個(gè)字節(jié)當(dāng)做一個(gè)完整的請(qǐng)求報(bào)文。
通常情況下,很少有client與server交互時(shí),直接使用定長(zhǎng)協(xié)議,可能會(huì)造成浪費(fèi)。例如你實(shí)際要發(fā)送的實(shí)際只有3個(gè)字節(jié),但是定長(zhǎng)協(xié)議設(shè)置的1024,那么可能你就要為這3個(gè)字節(jié)基礎(chǔ)上,在加1021個(gè)空格,以便server端可以解析這個(gè)請(qǐng)求。在下一節(jié)我們將要介紹的LengthFieldBasedFrameDecoder,支持動(dòng)態(tài)指定報(bào)文的長(zhǎng)度。