Java并发编程学习-日记7、第二个netty程序回显服务器

代码示例:

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通道流水线中。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值