netty 初探
——总结自《netty 实战》
netty 的版本是4
简单 server channelHandler:
@ChannelHandler.Sharable // 标识一个 channelHandler 可以被多个 channel 安全地调用。
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buffer = (ByteBuf)msg;
System.out.println("服务器收到消息:" + buffer.toString(CharsetUtil.UTF_8));
// 将接收到的消息写给发送者,而不冲刷出站消息。
ctx.write(buffer);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// 将未决消息冲刷到远程节点,并且关闭该 channel
ChannelFuture channelFuture = ctx.writeAndFlush(Unpooled.EMPTY_BUFFER);
channelFuture.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 打印异常栈,并关闭该 channel
cause.printStackTrace();
ctx.close();
}
}
每个 channel 都拥有一个与之相关联的 channelPipeline,其持有一个 channelHandler 的实例链。在默认情况下,channelHandler 会把对它的方法调用转发给链中的下一个 channelHandler。因此,如果 exceptionCaught() 方法没有被该链中的某处实现,那么所接收的异常会被传递到 channelPipeline 的尾端并被记录。因此,应用程序应该提高至少一个实现了 exceptionCaught() 方法的 channelHandler。
引导服务器:
public class NettyServer {
public static void main(String[] args) {
NettyServer nettyServer = new NettyServer();
nettyServer.start(8888);
}
public void start(int port){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
//将上面的线程组加入到 bootstrap 中
bootstrap.group(bossGroup,workGroup)
//将通道设置为异步的通道
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 因为 NettyServerHandler 被标注为 @Sharable,所以可以使用相同的实例
socketChannel.pipeline().addLast(new NettyServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG,200)
.childOption(ChannelOption.SO_KEEPALIVE,true);
// 异步绑定服务器,调用 sync() 方法阻塞等待直到绑定完成。
ChannelFuture future = bootstrap.bind(port).sync();
// 获取 channel 的 closeFuture,并且阻塞直到它完成。
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
简单 client channel:
@ChannelHandler.Sharable
public class NettyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("netty 活跃", CharsetUtil.UTF_8));
}
// 记录已接收的消息存储
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("客户端接收消息:" + msg.toString(CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
客户端
public class NettyClient {
public static void main(String[] args) {
NettyClient nettyClient = new NettyClient();
nettyClient.connect("localhost", 8888);
}
public void connect(String hostname,int port) {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline()
.addLast(new NettyClientHandler());
}
})
.option(ChannelOption.TCP_NODELAY,true);
// 连接到远程节点,阻塞等待直到连接完成。
ChannelFuture future = bootstrap.connect(hostname, port).sync();
// 阻塞,直到 channel 关闭。
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 关闭线程池并释放所有资源。
group.shutdownGracefully();
}
}
}