• Netty入门——组件(Channel)一


    一、channel的主要作用

    channel 中的方法作用
    close()用来关闭 channel
    closeFuture()处理 channel 的关闭,sync 方法作用是同步等待 channel 关闭,addListener 方法是异步等待 channel 关闭
    pipeline()添加处理器
    write()将数据写入
    writeAndFlush()将数据写入并刷出

    二、EventLoop处理io任务代码示例

    2.1、服务端代码示例

    • 引入pom依赖

       <dependency>
           <groupId>io.netty</groupId>
           <artifactId>netty-all</artifactId>
           <version>4.1.39.Final</version>
       </dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 服务端

      import io.netty.bootstrap.ServerBootstrap;
      import io.netty.buffer.ByteBuf;
      import io.netty.channel.ChannelHandlerContext;
      import io.netty.channel.ChannelInboundHandlerAdapter;
      import io.netty.channel.ChannelInitializer;
      import io.netty.channel.DefaultEventLoopGroup;
      import io.netty.channel.nio.NioEventLoopGroup;
      import io.netty.channel.socket.nio.NioServerSocketChannel;
      import io.netty.channel.socket.nio.NioSocketChannel;
      import lombok.extern.slf4j.Slf4j;
      
      import java.nio.charset.Charset;
      
      /**
       * @description: EventLoop处理io任务 服务端
       * @author: xz
       */
      @Slf4j
      public class EventLoopServer {
          public static void main(String[] args) {
              //创建一个独立的EventLoopGroup
              DefaultEventLoopGroup normalWorkers = new DefaultEventLoopGroup(2);
              //1、服务端启动器:负责组装netty组件
              new ServerBootstrap()
                      //2、将EventLoop分为boss和worker(即将EventLoop分工细化)
                      // boss即第1个参数,只负责accept事件; worker即第2个参数,只负责socketChannel上的读写
                      .group(new NioEventLoopGroup(1), new NioEventLoopGroup(2))
                      //3、选择服务器的 ServerSocketChannel 实现
                      .channel(NioServerSocketChannel.class)
                      //4、添加服务端处理器
                      .childHandler(
                          // 5. channel 代表和客户端进行数据读写的通道 Initializer 初始化,负责添加别的 handler
                          new ChannelInitializer<NioSocketChannel>() {
                          @Override
                          protected void initChannel(NioSocketChannel ch) throws Exception {
                              //6、添加具体 handler
                              ch.pipeline().addLast(normalWorkers,"handler1", new ChannelInboundHandlerAdapter() {
                                  @Override
                                  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                      //msg转ByteBuf
                                      ByteBuf buf = (ByteBuf) msg;
                                      //ByteBuf转字符串
                                      log.debug(buf.toString(Charset.defaultCharset()));
                                      //让消息传递给下一个handler
                                      ctx.fireChannelRead(msg);
                                  }
                              });
                          }
                      })
                      //7、绑定监听端口
                      .bind(8080);
          }
      }
      
      • 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
      • 49
      • 50
      • 51
      • 52
      • 53

    2.2、客户端代码示例

    • 客户端

      import io.netty.bootstrap.Bootstrap;
      import io.netty.buffer.ByteBufAllocator;
      import io.netty.channel.Channel;
      import io.netty.channel.ChannelInitializer;
      import io.netty.channel.nio.NioEventLoopGroup;
      import io.netty.channel.socket.nio.NioSocketChannel;
      import io.netty.handler.codec.string.StringEncoder;
      import lombok.extern.slf4j.Slf4j;
      import java.net.InetSocketAddress;
      /**
       * @description: EventLoop处理io任务 客户端
       * @author: xz
       */
      @Slf4j
      public class EventLoopClient {
          public static void main(String[] args) throws InterruptedException {
              // 1. 客户端启动器
              Channel channel = new Bootstrap()
                      // 2. 添加 EventLoop(事件循环)
                      .group(new NioEventLoopGroup(1))
                      // 3. 选择客户端的 SocketChannel 实现
                      .channel(NioSocketChannel.class)
                      // 4. 添加客户端处理器
                      .handler(new ChannelInitializer<NioSocketChannel>() {
                          // 在连接建立后被调用
                          @Override
                          protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                              //9. 消息会经过通道 handler 处理,这里是将 String => ByteBuf 发出
                              nioSocketChannel.pipeline().addLast(new StringEncoder());
                          }
                      })
                      //5. 连接到服务器
                      .connect(new InetSocketAddress("localhost", 8080))
                      //6. 等待 connect 建立连接完毕
                      .sync()
                      //7. 连接对象
                      .channel();
              System.out.println("打印channel对象==="+channel);
              //8. 发送数据
              channel.writeAndFlush(ByteBufAllocator.DEFAULT.buffer().writeBytes("aaaaaa".getBytes()));
          }
      }
      
      • 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

    2.3、服务端和客户端查看控制台输出结果

    • 先启动服务端,再启动客户端,查看客户端控制台输出,结果如下:
      在这里插入图片描述
    • 再查看服务端控制台输出,结果如下:
      在这里插入图片描述

    三、ChannelFuture连接问题代码示例

    3.1、服务端代码示例

    • 同步2.1步骤中的代码

    3.2、客户端代码示例

    • 将2.2步骤中客户端代码拆开,代码如下

      import io.netty.bootstrap.Bootstrap;
      import io.netty.channel.Channel;
      import io.netty.channel.ChannelFuture;
      import io.netty.channel.ChannelFutureListener;
      import io.netty.channel.ChannelInitializer;
      import io.netty.channel.nio.NioEventLoopGroup;
      import io.netty.channel.socket.nio.NioSocketChannel;
      import io.netty.handler.codec.string.StringEncoder;
      import io.netty.handler.logging.LogLevel;
      import io.netty.handler.logging.LoggingHandler;
      import lombok.extern.slf4j.Slf4j;
      
      import java.net.InetSocketAddress;
      
      /**
       * @description: EventLoop处理io任务中ChannelFuture连接问题
       * @author: xz
       */
      @Slf4j
      public class ChannelFutureClient {
          public static void main(String[] args) throws InterruptedException {
              client1();
          }
           /**
           * 将客户端代码拆开
           * ChannelFuture连接问题 : connect 方法是异步的,意味着不等连接建立,方法执行就返回了。因此 channelFuture 对象中不能【立刻】获得到正确的 Channel 对象
           * */
          public static void client1() throws InterruptedException {
              ChannelFuture channelFuture = new Bootstrap()
                      .group(new NioEventLoopGroup(1))
                      .channel(NioSocketChannel.class)
                      .handler(new ChannelInitializer<NioSocketChannel>() {
                          @Override
                          protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                              nioSocketChannel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
                              nioSocketChannel.pipeline().addLast(new StringEncoder());
                          }
                      })
                      //1、连接到服务器
                      //异步非阻塞,main方法发起了调用,真正执行connect是nio线程
                      //返回的是 ChannelFuture 对象,它的作用是利用 channel() 方法来获取 Channel 对象
                      .connect(new InetSocketAddress("localhost", 8080));
              //无阻塞向下执行获取channel
              Channel channel = channelFuture.channel();
              log.info("连接未建立,channel对象====={}",channel);
              channel.writeAndFlush("aaaaaaaaaaaaaaaaaa");
          }
       }
      
      • 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

    3.3、服务端和客户端查看控制台输出结果

    • 先启动服务端,再启动客户端,查看客户端控制台输出,结果如下:
      在这里插入图片描述

    • 再查看服务端控制台输出,结果如下:

      在这里插入图片描述

    3.4、ChannelFuture出现连接问题的原因

    • 由上述代码示例可知,connect 方法是异步的,意味着不等连接建立,方法执行就返回了。因此 channelFuture 对象中不能【立刻】获得到正确的 Channel 对象。

    四、ChannelFuture连接问题的处理方式一(使用sync方法同步处理结果)

    4.1、服务端代码示例

    • 同步2.1步骤中的代码

    4.2、客户端代码示例

    • 将3.2步骤中客户端代码进行修改,代码如下

      import io.netty.bootstrap.Bootstrap;
      import io.netty.channel.Channel;
      import io.netty.channel.ChannelFuture;
      import io.netty.channel.ChannelFutureListener;
      import io.netty.channel.ChannelInitializer;
      import io.netty.channel.nio.NioEventLoopGroup;
      import io.netty.channel.socket.nio.NioSocketChannel;
      import io.netty.handler.codec.string.StringEncoder;
      import io.netty.handler.logging.LogLevel;
      import io.netty.handler.logging.LoggingHandler;
      import lombok.extern.slf4j.Slf4j;
      
      import java.net.InetSocketAddress;
      
      /**
       * @description: EventLoop处理io任务中ChannelFuture连接问题及处理结果
       * @author: xz
       */
      @Slf4j
      public class ChannelFutureClient {
          public static void main(String[] args) throws InterruptedException {
              client2();
          }
          public static void client2() throws InterruptedException {
              ChannelFuture channelFuture = new Bootstrap()
                      .group(new NioEventLoopGroup(1))
                      .channel(NioSocketChannel.class)
                      .handler(new ChannelInitializer<NioSocketChannel>() {
                          @Override
                          protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                              nioSocketChannel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
                              nioSocketChannel.pipeline().addLast(new StringEncoder());
                          }
                      })
                      //1、连接到服务器
                      //异步非阻塞,main方法发起了调用,真正执行connect是nio线程
                      .connect(new InetSocketAddress("localhost", 8080));
              //无阻塞向下执行获取channel
              Channel channel = channelFuture.channel();
              log.info("连接未建立,打印channel对象====={}",channel);//2
              //使用sync方法同步处理结果,阻塞当前线程,直到nio线程连接建立完毕
              channelFuture.sync(); // 3
              log.info("建立连接后,打印channel对象====={}",channel); // 4
              channel.writeAndFlush("bbbbbbbbb");
          }
      }
      
      • 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

    4.3、服务端和客户端查看控制台输出结果

    • 先启动服务端,再启动客户端,查看客户端控制台输出,结果如下:
      在这里插入图片描述

    • 再查看服务端控制台输出,结果如下:
      在这里插入图片描述

    4.4、客户端代码示例标注位置解释

    • 执行到 2 位置时,连接未建立,打印[id: 0x4de78375]
    • 执行到 3 位置时,sync 方法是同步等待连接建立完成
    • 执行到 4 位置时,连接肯定建立了,打印[id: 0x4de78375, L:/127.0.0.1:53147 - R:localhost/127.0.0.1:8080]

    五、ChannelFuture连接问题的处理方式二(使用addListener方法异步处理结果)

    5.1、服务端代码示例

    • 同步2.1步骤中的代码

    5.2、客户端代码示例

    • 将3.2步骤中客户端代码进行修改,代码如下

      import io.netty.bootstrap.Bootstrap;
      import io.netty.channel.Channel;
      import io.netty.channel.ChannelFuture;
      import io.netty.channel.ChannelFutureListener;
      import io.netty.channel.ChannelInitializer;
      import io.netty.channel.nio.NioEventLoopGroup;
      import io.netty.channel.socket.nio.NioSocketChannel;
      import io.netty.handler.codec.string.StringEncoder;
      import io.netty.handler.logging.LogLevel;
      import io.netty.handler.logging.LoggingHandler;
      import lombok.extern.slf4j.Slf4j;
      
      import java.net.InetSocketAddress;
      
      /**
       * @description: EventLoop处理io任务中ChannelFuture连接问题及处理结果
       * @author: xz
       */
      @Slf4j
      public class ChannelFutureClient {
          public static void main(String[] args) throws InterruptedException {
              client3();
          }
          public static void client3() throws InterruptedException {
              ChannelFuture channelFuture = new Bootstrap()
                      .group(new NioEventLoopGroup(1))
                      .channel(NioSocketChannel.class)
                      .handler(new ChannelInitializer<NioSocketChannel>() {
                          @Override
                          protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                              nioSocketChannel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
                              nioSocketChannel.pipeline().addLast(new StringEncoder());
                          }
                      })
                      //1、连接到服务器
                      //异步非阻塞,main方法发起了调用,真正执行connect是nio线程
                      .connect(new InetSocketAddress("localhost", 8080));
              //无阻塞向下执行获取channel
              Channel channel = channelFuture.channel();
              log.info("连接未建立,打印channel对象====={}",channel);  // 2
              channelFuture.addListener((ChannelFutureListener) future-> {
                  log.info("建立连接后,打印channel对象====={}",future.channel());//3
                  channel.writeAndFlush("ccccccccccc");
              });
          }
      }
      
      • 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

    5.3、服务端和客户端查看控制台输出结果

    • 先启动服务端,再启动客户端,查看客户端控制台输出,结果如下:
      在这里插入图片描述

    • 再查看服务端控制台输出,结果如下:
      在这里插入图片描述

    5.4、客户端代码示例标注位置解释

    • 执行到 2 位置时,连接未建立,打印[id: 0x01485347]
    • ChannelFutureListener 会在连接建立时被调用(其中 operationComplete 方法),因此执行到 3 位置时,连接肯定建立了,打印[id: 0x01485347, L:/127.0.0.1:53380 - R:localhost/127.0.0.1:8080]
  • 相关阅读:
    Css 设置从上到下的渐变色: 0到70%为yellow,然后线性地变成透明。
    第三章 MATLAB的使用
    JAVA毕业设计河东街摊位管理系统计算机源码+lw文档+系统+调试部署+数据库
    移动Web:Flex布局、移动端适配、视口、二倍图
    【JAVA】String类
    mysql数据库面试相关问题
    MSE 治理中心重磅升级-流量治理、数据库治理、同 AZ 优先
    【Linux 之二】Ubuntu下开发环境的搭建(NFS \ SSH \ FTP \ Smba \ ...)
    Symfony 控制台命令教程
    【JavaEE】HTTP协议(什么是HTTP?、HTTP格式、form表单和ajax构造HTTP)
  • 原文地址:https://blog.csdn.net/li1325169021/article/details/127652579