FixedLengthFrameDecoder是固定長度解碼器,它根據指定的長度自動對消息進行解碼,開發者不需要考慮TCP拆包/粘包問題。
下面通過一個例子進行說明。
服務端:
在服務端的ChannelPipleline中增加FixedLengthFrameDecoder,長度設定為20,然後依次增加字元串解碼器和自定義的EchoServerHandler。
/**
* 使用FixedLengthFrameDecoder解碼器解決TCP粘包/拆包問題。
*
* @author j.tommy
* @version 1.0
* @date 2017/11/18
*/
public class EchoServer {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, )
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new FixedLengthFrameDecoder()); // 固定長度解碼器,長度設定為20
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(new EchoServerHandler());
}
});
try {
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
class EchoServerHandler extends ChannelHandlerAdapter {
private int counter;
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String body = (String) msg;
System.out.println("接收到用戶端請求:" + body + ",counter=" + ++counter);
// 響應用戶端
ByteBuf resp = Unpooled.copiedBuffer(body.getBytes());
ctx.write(resp);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
}
用戶端:
在用戶端的ChannelPipleline中增加FixedLengthFrameDecoder,長度設定為20,然後依次增加字元串解碼器和自定義的EchoClientHandler。
/**
* @author j.tommy
* @version 1.0
* @date 2017/11/18
*/
public class EchoClient {
public static void main(String[] args) {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY,true).handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new FixedLengthFrameDecoder());
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
try {
ChannelFuture f = b.connect("127.0.0.1",).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
group.shutdownGracefully();
}
}
}
class EchoClientHandler extends ChannelHandlerAdapter {
private int counter = ;
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String req = "hello,this message is from client.";
ByteBuf sendBuf = null;
for (int i=;i<;i++) {
sendBuf = Unpooled.copiedBuffer(req.getBytes());
ctx.writeAndFlush(sendBuf);
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String resp = (String) msg;
System.out.println("接收到服務端響應:" + resp + ",counter=" + ++counter);
}
}
示例中,用戶端發送的消息是hello,this message is from client.
因為按照固定長度截取,是以服務端接收到的是這樣的:

參考《Netty權威指南》