• Netty实践-- echo


    Netty源码实践

    学习netty,可以从netty源码的 netty-example 模块开始。

    netty-example 有一个例子 echo,非常适合入门学习。

    这里稍微改造一下,用作示例学习。

    引入依赖包:

            
                io.netty
                netty-all
                4.1.29.Final
            
    
    • 1
    • 2
    • 3
    • 4
    • 5

    服务端

    服务端收到客户端的消息后,会进行响应。

    • EchoServer:
    /**
     * 服务端收到客户端的消息后,会进行响应。
     */
    public final class EchoServer {
        /**
         * 端口
         */
        static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
    
        public static void main(String[] args) throws Exception {
            // 配置 EventLoopGroup
            // 主从 Reactor 多线程模式,bossGroup是 主 Reactor,workerGroup是 从Reactor
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                //初始化服务器的引导类 ServerBootstrap
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                //指定 EventLoopGroup
                serverBootstrap.group(bossGroup, workerGroup)
                    //指定 channel
                 .channel(NioServerSocketChannel.class)
                 .option(ChannelOption.SO_BACKLOG, 100)
                    //指定 ChannelHandler,用于处理 channel
                 .handler(new LoggingHandler(LogLevel.INFO))
                 .childHandler(new ChannelInitializer() {
                     @Override
                     public void initChannel(SocketChannel ch) throws Exception {
                         //ChannelPipeline,基于责任链模式,可以添加多个 ChannelHandler
                         ChannelPipeline channelPipeline = ch.pipeline();
                         //channelPipeline.addLast(new LoggingHandler(LogLevel.INFO));
                         //ChannelHandler,用于处理 channel,实现对接收的数据的处理,实现业务逻辑。
                         channelPipeline.addLast(new EchoServerHandler());
                     }
                 });
    
                // 开启服务器,将服务器绑定到它要监听连接请求的端口上
                ChannelFuture channelFuture = serverBootstrap.bind(PORT).sync();
    
                // 等待直到服务器socket关闭
                channelFuture.channel().closeFuture().sync();
            } finally {
                //关闭所有 eventLoop,终止线程
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • EchoServerHandler:

    ChannelHandler,用于处理 channel,实现业务逻辑

    /**
     * 服务端的 ChannelHandler.
     *
     * ChannelHandler,用于处理 channel,实现对接收的数据的处理,实现业务逻辑。
     * 继承 ChannelInboundHandlerAdapter,用来定义响应入站事件的方法。
     *
     */
    @Sharable
    public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    
        /**
         * channelRead() :读取 channel 传入的消息
         *
         */
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            ByteBuf buf= (ByteBuf) msg;
            log.info("客户端发来的消息:"+ buf.toString(CharsetUtil.UTF_8) +"\n");
        }
    
        /**
         * channelReadComplete() :表示当前 ChannelHandler 读取完毕.
         * 执行后会自动跳转到 ChannelPipeline 中的下一个 ChannelHandler.
         *
         */
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) {
            //向客户端返回数据,writeAndFlush() 也可以拆分成 write(msg) 和 flush()
            ctx.writeAndFlush(Unpooled.copiedBuffer("见到你,我也很高兴^_^",CharsetUtil.UTF_8));
        }
    
        /**
         * exceptionCaught(): 在读取操作期间,有异常抛出时会调用。
         *
         */
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            // 发生异常时关闭连接
            cause.printStackTrace();
            ctx.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    客户端

    在以下示例中,客户端会向服务端发送消息。

    • EchoClient:
    /**
     * 客户端。发送数据给服务端,并接收服务端的响应。
     *
     */
    public final class EchoClient {
    
        static final String HOST = System.getProperty("host", "127.0.0.1");
        static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
        static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));
    
        public static void main(String[] args) throws Exception {
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.group(group)
                 .channel(NioSocketChannel.class)
                 .option(ChannelOption.TCP_NODELAY, true)
                 .handler(new ChannelInitializer() {
                     @Override
                     public void initChannel(SocketChannel socketChannel) throws Exception {
                         ChannelPipeline channelPipeline = socketChannel.pipeline();
                         //channelPipeline.addLast(new LoggingHandler(LogLevel.INFO));
                         //ChannelHandler,用于处理 channel,实现对接收的数据的处理,实现业务逻辑。
                         channelPipeline.addLast(new EchoClientHandler());
                     }
                 });
    
                // 开启客户端,连接服务端的端口
                ChannelFuture channelFuture = bootstrap.connect(HOST, PORT).sync();
    
                channelFuture.channel().closeFuture().sync();
            } finally {
                group.shutdownGracefully();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • EchoClientHandler:
    /**
     * 客户端 的 ChannelHandler.
     */
    public class EchoClientHandler extends ChannelInboundHandlerAdapter {
    
    
        /**
         * channelActive() 客户端跟服务器的连接建立之后将被调用.
         *
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            ByteBuf firstMessage = Unpooled.buffer(EchoClient.SIZE);
            byte[] bytes = "见到你很高兴^_^\n".getBytes(CharsetUtil.UTF_8);
            firstMessage.writeBytes(bytes);
            //向服务器发送数据
            ctx.writeAndFlush(firstMessage);
        }
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            ByteBuf buf = (ByteBuf) msg;
            log.info("服务器发来的消息:" + buf.toString(CharsetUtil.UTF_8));
        }
    
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) {
            ctx.flush();
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    执行:

    先启动服务端,然后再启动客户端。

    服务端收到客户端的信息:

    11:25:03.081 [nioEventLoopGroup-3-1] INFO com.example.demo.netty.echo.EchoServerHandler - 客户端发来的消息:见到你很高兴^_^
    
    • 1

    客户端收到服务端的回复:

    11:25:03.091 [nioEventLoopGroup-2-1] INFO com.example.demo.netty.echo.EchoClientHandler - 服务器发来的消息:见到你,我也很高兴^_^
    
    • 1

    Netty常用类

    以上的这个示例,用到了 Netty常用的类,

    详情见:https://blog.csdn.net/sinat_32502451/article/details/133934402

    参考资料:

    https://zhuanlan.zhihu.com/p/415450910

  • 相关阅读:
    深入探索:AbstractQueuedSynchronizer 同步器的神秘面纱
    pyautogui模拟鼠标拖动选中文字的基本知识(附Demo)
    (持续更新中!)详解【计算机类&面试真题】军队文职考试 ——第二期(真题+解析)| 网络协议的三个核心要素;交互式系统中的非剥夺策略;中断和陷入的区别;可变分区管理中的硬件机制;数据库系统的优点
    论文笔记: 度量学习之 ITML (理解ing)
    基于stm32单片机自动灭火火灾报警装置Proteus仿真
    杰理之蓝牙连接设备的 API【篇】
    Chapter 4 k-近邻算法与朴素贝叶斯
    @EqualsAndHashCode注解!!!
    Android 唤醒屏幕的方式:屏幕锁 - WakeLock / FLAG_KEEP_SCREEN_ON
    JavaScript分支语句(if、三元表达式、switch)
  • 原文地址:https://blog.csdn.net/sinat_32502451/article/details/133934934