代码示例:
public class NettyEchoServer { private final int serverPort; ServerBootstrap b = new ServerBootstrap(); public NettyEchoServer(int port) { this.serverPort = port; } public void runServer() { //创建reactor 线程组 EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1); EventLoopGroup workerLoopGroup = new NioEventLoopGroup(); try { b.group(bossLoopGroup, workerLoopGroup); //1 设置reactor 线程组 b.channel(NioServerSocketChannel.class); //2 设置nio类型的channel b.localAddress(serverPort); //3 设置监听端口 //4 设置通道的参数 b.option(ChannelOption.SO_KEEPALIVE, true); b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); //5 装配子通道流水线 b.childHandler(new ChannelInitializer<SocketChannel>() { //有连接到达时会创建一个channel protected void initChannel(SocketChannel ch) throws Exception { // pipeline管理子通道channel中的Handler // 向子channel流水线添加一个handler处理器 ch.pipeline().addLast(NettyEchoServerHandler.INSTANCE); } }); // 6 开始绑定server,通过调用sync同步方法阻塞直到绑定成功 ChannelFuture channelFuture = b.bind().sync(); Logger.info(" 服务器启动成功,监听端口: " + channelFuture.channel().localAddress()); // 7 等待通道关闭的异步任务结束,服务监听通道会一直等待通道关闭的异步任务结束 ChannelFuture closeFuture = channelFuture.channel().closeFuture(); closeFuture.sync(); } catch (Exception e) { e.printStackTrace(); } finally { // 8 优雅关闭EventLoopGroup,释放掉所有资源包括创建的线程 workerLoopGroup.shutdownGracefully(); bossLoopGroup.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { new NettyEchoServer(9000).runServer(); } } @ChannelHandler.Sharable //一个Handler可以被多个通道安全地共享 public class NettyEchoServerHandler extends ChannelInboundHandlerAdapter { public static final NettyEchoServerHandler INSTANCE = new NettyEchoServerHandler(); @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf in = (ByteBuf) msg; Logger.info("msg type: " + (in.hasArray()?"堆内存":"直接内存")); int len = in.readableBytes(); byte[] arr = new byte[len]; in.getBytes(0, arr); Logger.info("server received: " + new String(arr, "UTF-8")); //写回数据,异步任务 Logger.info("写回前,msg.refCnt:" + ((ByteBuf) msg).refCnt()); ChannelFuture f = ctx.writeAndFlush(msg); f.addListener((ChannelFuture futureListener) -> { Logger.info("写回后,msg.refCnt:" + ((ByteBuf) msg).refCnt()); }); } } |
public class NettyEchoClient { private int serverPort; private String serverIp; Bootstrap b = new Bootstrap(); public NettyEchoClient(String ip, int port) { this.serverPort = port; this.serverIp = ip; } public void runClient() { EventLoopGroup workerLoopGroup = new NioEventLoopGroup();//创建reactor 线程组 try { b.group(workerLoopGroup); //1 设置reactor 线程组 b.channel(NioSocketChannel.class); //2 设置nio类型的channel b.remoteAddress(serverIp, serverPort); //3 设置监听端口 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); //4 设置通道的参数 //5 装配子通道流水线 b.handler(new ChannelInitializer<SocketChannel>() { //有连接到达时会创建一个channel protected void initChannel(SocketChannel ch) throws Exception { // pipeline管理子通道channel中的Handler // 向子channel流水线添加一个handler处理器 ch.pipeline().addLast(NettyEchoClientHandler.INSTANCE); } }); ChannelFuture f = b.connect(); f.addListener((ChannelFuture futureListener) -> { if (futureListener.isSuccess()) { Logger.info("EchoClient客户端连接成功!"); } else { Logger.info("EchoClient客户端连接失败!"); }
}); f.sync();// 阻塞,直到连接完成 Channel channel = f.channel(); Scanner scanner = new Scanner(System.in); Print.tcfo("请输入发送内容:"); while (scanner.hasNext()) { //获取输入的内容 String next = scanner.next(); byte[] bytes = (Dateutil.getNow() + " >>" + next).getBytes("UTF-8"); //发送ByteBuf ByteBuf buffer = channel.alloc().buffer(); buffer.writeBytes(bytes); channel.writeAndFlush(buffer); Print.tcfo("请输入发送内容:"); } } catch (Exception e) { e.printStackTrace(); } finally { // 优雅关闭EventLoopGroup, // 释放掉所有资源包括创建的线程 workerLoopGroup.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { new NettyEchoClient(127.0.0.1, 9000).runClient(); } } @ChannelHandler.Sharable public class NettyEchoClientHandler extends ChannelInboundHandlerAdapter { public static final NettyEchoClientHandler INSTANCE = new NettyEchoClientHandler(); @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf in = (ByteBuf) msg; int len = in.readableBytes(); byte[] arr = new byte[len]; in.getBytes(0, arr); Logger.info("client received: " + new String(arr, "UTF-8")); in.release(); } } |
注解:@ChannelHandler.Sharable的作用是标注一个Handler实例可以被多个通道安全地共享。
什么叫作Handler共享呢?
就是多个通道的流水线可以加入同一个Handler业务处理器实例。而这种操作,Netty默认是不允许。
- 如果没有加@ChannelHandler.Sharable注解,试图将同一个Handler实例添加到多个ChannelPipeline通道流水线时,Netty将会抛出异常。
- 同一个通道上的所有业务处理器,只能被同一个线程处理。所以,不是@Sharable共享类型的业务处理器,在线程的层面是安全的,不需要进行线程的同步控制。而不同的通道,可能绑定到多个不同的EventLoop反应器线程。因此,加上了@ChannelHandler. Sharable注解后的共享业务处理器的实例,可能被多个线程并发执行。这样,就会导致一个结果:@Sharable共享实例不是线程层面安全的。显而易见,@Sharable共享的业务处理器,如果需要操作的数据不仅仅是局部变量,则需要进行线程的同步控制,以保证操作是线程层面安全。
- ChannelHandlerAdapter提供了实用方法——isSharable()。如果其对应的实现加上了@Sharable注解,那么这个方法将返回true,表示它可以被添加到多个ChannelPipeline通道流水线中。