前面我们介绍了利用LineBasedFrameDecoder和DelemiterBasedFrameDecoder解决TCP的粘包/拆包的问题,
现在我们继续介绍Netty的最后一种解码器--FixedLengthFrameDecoder。
利用FixedLengthFrameDecoder解码器,无论一次接收到多少数据报,它都会按照构造函数中设置的固定长度进行解码,如果是半包消息,FixedLengthFrameDecoder会缓存半包消息并等待下个包到达后进行拼包,直到读取到一个完整的包。
1. EchoServer.java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
public class EchoServer {
private final static int port = 8080;
public static void main(String[] args) {
start();
}
private static void start() {
final EchoServerHandler serverHandler = new EchoServerHandler();
// 创建EventLoopGroup
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 创建EventLoopGroup
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(
new FixedLengthFrameDecoder(20));
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(serverHandler);
}
});
try {
// 异步地绑定服务器;调用sync方法阻塞等待直到绑定完成
ChannelFuture f = b.bind(port).sync();
// 获取Channel的CloseFuture,并且阻塞当前线程直到它完成
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 优雅的关闭EventLoopGroup,释放所有的资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
2. EchoServerHandler.java
3. EchoClient.javaimport io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; @Sharable public class EchoServerHandler extends ChannelInboundHandlerAdapter{ private int counter = 0; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String body = (String )msg; System.out.println( "This is " + ++counter +" times receive client [" + body +"]" ); ByteBuf resp = Unpooled.copiedBuffer(body.getBytes()); ctx.writeAndFlush(resp); } /** * 异常处理 * @param ctx * @param cause * @throws Exception */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { //打印异常栈跟踪 cause.printStackTrace(); // 关闭该Channel ctx.close(); } }
4. EchoClientHandler.javaimport io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.FixedLengthFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import java.net.InetSocketAddress; public class EchoClient { private final static String HOST = "localhost"; private final static int PORT = 8080; public static void start() { EventLoopGroup group = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(20)); socketChannel.pipeline().addLast(new StringDecoder()); socketChannel.pipeline().addLast(new EchoClientHandler()); } }); try { ChannelFuture f = bootstrap.connect(HOST,PORT).sync(); f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); }finally { group.shutdownGracefully(); } } public static void main(String[] args) { start(); } }
分别运行EchoServer和EchoClient,控制台的运行结果如下import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; @Sharable public class EchoClientHandler extends ChannelInboundHandlerAdapter { private int counter; static final String ECHO_REQ = "Hi, Welcome to Netty World!"; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String body = (String) msg; System.out.println( "This is "+ ++counter + " times receive server:[" + body +"]" ); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { for (int i = 0; i < 10; i++) { ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes())); } } }
服务端运行结果:
客户端运行结果:This is 1 times receive client [Hi, Welcome to Netty] This is 2 times receive client [ World!Hi, Welcome t] This is 3 times receive client [o Netty World!Hi, We] This is 4 times receive client [lcome to Netty World] This is 5 times receive client [!Hi, Welcome to Nett] This is 6 times receive client [y World!Hi, Welcome ] This is 7 times receive client [to Netty World!Hi, W] This is 8 times receive client [elcome to Netty Worl] This is 9 times receive client [d!Hi, Welcome to Net] This is 10 times receive client [ty World!Hi, Welcome] This is 11 times receive client [ to Netty World!Hi, ] This is 12 times receive client [Welcome to Netty Wor] This is 13 times receive client [ld!Hi, Welcome to Ne]
This is 1 times receive server:[Hi, Welcome to Netty] This is 2 times receive server:[ World!Hi, Welcome t] This is 3 times receive server:[o Netty World!Hi, We] This is 4 times receive server:[lcome to Netty World] This is 5 times receive server:[!Hi, Welcome to Nett] This is 6 times receive server:[y World!Hi, Welcome ] This is 7 times receive server:[to Netty World!Hi, W] This is 8 times receive server:[elcome to Netty Worl] This is 9 times receive server:[d!Hi, Welcome to Net] This is 10 times receive server:[ty World!Hi, Welcome] This is 11 times receive server:[ to Netty World!Hi, ] This is 12 times receive server:[Welcome to Netty Wor] This is 13 times receive server:[ld!Hi, Welcome to Ne]
节选自《Netty权威指南第二版》, 代码部分有部分改动。