关于java:Netty全家宴带你实现第一个Netty-Demo

40次阅读

共计 5755 个字符,预计需要花费 15 分钟才能阅读完成。

微信公众号:大黄奔跑
关注我,可理解更多乏味的面试和编程相干问题。

写在之前

之前介绍了 Netty 开胃菜的三道小菜,别离为 Nio buffer、Nio Channel、Nio Selector。

后面说了这么多,还在 Netty 门外彷徨,明天会给大家展现一个 Netty 真面目。然而我不打算 Netty 开篇就扎根于某个菜品中。

本篇会带着大家介绍第一个 Netty demo,后续文章的剖析都是基于这一个 Demo 开展,第一次看这个 demo 的同学可能会很多中央看不懂,本篇文章先按下不表,后续问问会一一介绍,防止刚开始开宴就沉迷于某一道下酒菜中,疏忽了整个口味俱佳的美味。

封面来源于李安饮食男女的全家宴,每次看这部电影口水直流。

次要目标

次要利用 Netty 来展现一个客户端与服务器连贯的应用程序,程序目标很简略,客户端将音讯发送给服务器、服务器再将音讯返回给客户端。

尽管目标简略,然而这个 demo 意义重点,后续咱们会沿着这个 demo 一步步走上来,试图拆解 netty 的各个细节。

服务器端建设

Netty 奇妙地将数据处理和具体的服务器启动等连贯过程拆散开来,使用者能够自定义数据处理工具,而每个程序的启动类大同小异,能够做到真正的共用。

所以要实现服务器端配置次要有两局部:

  1. 服务器的启动代码——次要将服务器绑定到须要监听的连贯申请的端口上
  2. 具体的业务代码——上一步说的数据处理逻辑,在 Netty 中用一系列 handler 实现

1. 启动代码实现

为了防止代码过长,省略掉了临时不思考的代码,尽可能把核心内容展现给大家。

public class NettyServer {public static void main(String[] args) {

    // 1. 首先创立 两个线程组 BossGroup 和 WorkerGroup
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workGroup = new NioEventLoopGroup();

    // 2. 创立服务端启动类,配置启动参数
    ServerBootstrap serverBootstrap = new ServerBootstrap();

    // 3. 配置具体的参数,配置具体参数
    /**
     * 3.1 配置 group
     * 3.2 应用 NioServerSocketChannel 作为服务器的通道实现
     * 3.3 设置具体的 Handler
     */
    serverBootstrap.group(bossGroup, workGroup)
      .channel(NioServerSocketChannel.class)
      .childHandler(new ChannelInitializer < SocketChannel > () {
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
          //4. 给 pipeline 增加处理器,每当有连贯 accept 时,就会运行到此处。socketChannel.pipeline().addLast(new NettyServerHandler());
        }
      });

    System.out.println("server is ready……");

    // 5. 绑定端口并且同步,生成了一个 ChannelFuture 对象
    ChannelFuture channelFuture = serverBootstrap.bind(8887).sync();

    // 6. 对 channel 进行敞开,留神这里全部都是异步操作
    channelFuture.channel().closeFuture().sync();}
}

留神下面的示例代码中,最初增加了一个 childHandler,其中退出了一个 new NettyServerHandler(),后面说过,大家权且能够将其看作是 具体业务逻辑代码处理器,该 Handler 须要用户自定义。

2. 业务逻辑——Handler

服务器 Handler 默认都是解决服务器响应传入的音讯,自定义的 Handler 能够通过继承 Netty 预置 ChannelInboundHandlerAdapter,至于为何须要继承该类,此处 按下不表 + 1,后续会写文章具体介绍。

public class NettyServerHandler extends ChannelInboundHandlerAdapter {

  /**
   * channelRead()——对于每个传入的音讯都须要调用
   *
   * @param ctx
   * @param msg
   */
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) {
    // 压缩
    ByteBuf in = (ByteBuf) msg;
    // 将音讯记录到控制台
    System.out.println("Server received:" + in .toString(CharsetUtil.UTF_8));
    // 将接管到的音讯写给发送者
    ctx.write(in);
  }

  /**
   * 告诉 ChannelInboundHandler 最初一次对 channelRead()的调用是以后批量读取中的最初一条音讯
   *
   * @param ctx
   * @throws Exception
   */
  @Override
  public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    // 将未决音讯冲刷到近程节点,并且敞开该 Channel
    ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
      .addListener(ChannelFutureListener.CLOSE);
  }

  /**
   * 在读取期间,有异样的时候会调用
   *
   * @param ctx
   * @param cause
   */
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    // 打印异样栈跟踪
    cause.printStackTrace();
    // 敞开该 Channel
    ctx.close();}
}

上来劈头盖脸写了两段代码,并且不加任何介绍,多多少少有一些耍流氓,因而,让咱们回顾一下方才两段代码实现服务器实现的次要步骤,次要能够划分为如下六步,这里的步骤能够好好了解一下,后续所有的文章的剖析其实都是来源于此六步,这六步能够说是 Netty 的六脉神剑。

(1)NettyServerHandler 实现了业务逻辑

(2)NettyServer 次要是启动类,用于疏导服务器,疏导过程能够细化为六小步。

  • 创立两个用于解决连贯和业务的线程组。EventLoopGroup bossGroup = new NioEventLoopGroup();
  • 创立服务端启动类,以疏导和绑定服务器。ServerBootstrap serverBootstrap = new ServerBootstrap();
  • 指定所应用的 NIO 传输 Channel。.channel(NioServerSocketChannel.class)
  • 应用 Handler 实例,解决具体业务逻辑。
.childHandler(new ChannelInitializer < SocketChannel > () {
  @Override
  protected void initChannel(SocketChannel socketChannel) throws Exception {
    //4. 给 pipeline 增加处理器,每当有连贯 accept 时,就会运行到此处。socketChannel.pipeline().addLast(new NettyServerHandler());
  }
});
  • 异步绑定服务器,阻塞期待直到绑定实现。ChannelFuture channelFuture = serverBootstrap.bind(8887).sync();
  • 敞开 Channel。channelFuture.channel().closeFuture().sync();

至此服务器端代码,曾经实现实现。这里疏忽了局部异样解决逻辑,次要是防止被太多无关紧要内容打乱,想要试验的同学能够私信我提供残缺 demo。

客户端实现

客户端次要解决的逻辑同样划分为两局部,业务逻辑和疏导类,整体思路与服务器端相似。

整体步骤大略 分为四步:

  1. 连贯到服务器
  2. 发送音讯给服务器
  3. 对于每个音讯,期待并接管从服务器发回的音讯
  4. 敞开与服务器连贯

1. 客户端启动实现

整体解决思路与服务器端相似,不同的是,客户端是应用服务器 ip 和端口连贯到近程地址,而不是绑定到一个始终被监听的端口。

public class NettyClient {public static void main(String[] args) {

    // 1. 客户端须要一个事件循环组
    EventLoopGroup clientGroup = new NioEventLoopGroup();

    try {
      // 2. 创立客户端启动对象
      Bootstrap bootstrap = new Bootstrap();

      // 3. 设置启动器的相干参数
      /**
       * 3.1 设置线程组
       * 3.2 设置客户端通道的实现类(应用反射)* 3.3 设置具体的解决 handler
       */
      bootstrap.group(clientGroup)
        .channel(NioSocketChannel.class)
        .handler(new ChannelInitializer < SocketChannel > () {
          @Override
          protected void initChannel(SocketChannel socketChannel) throws Exception {
            // 增加客户端解决逻辑 Handler
            socketChannel.pipeline().addLast(new NettyClientHandler());

          }
        });
      System.out.println("客户端 OK...");

      // 5. 连贯服务器,留神这里全部都是异步的
      ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8887).sync();

      // 6. 敞开通道连贯监听
      channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();
    } finally {clientGroup.shutdownGracefully();
    }
  }
}

整体流程能够参见服务器启动流程,说说不同之处。

  1. 为了初始化客户端启动类,创立一个 bootstrap,这是专门用户解决客户端启动的类。
  2. 服务器端创立了两个 NioEventLoopGroup 线程组,而客户端这里只创立了一个线程组,具体起因此处按下不表(挖坑 +1),后续会给大家补上。
  3. 连贯服务器近程连贯时,同时应用了 ip + host 信息,而服务器只是绑定了端口号。

2. 业务逻辑——Handler

与服务器相似,客户端同样须要继承 ChannelInboundHandlerAdapter 用于客户端解决数据逻辑,不过须要实现的办法却大雷同。

  1. 重写 channelActive()办法——用于与服务器建设连贯之后被调用,个别用于建设之后发送音讯。
  2. 重写 channelRead()办法——从服务器接管到一条音讯后被调用
  3. 重写 exceptionCaught()办法——用于产生异样时被调用
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

  /**
   * 用于与服务器建设连贯之后被调用,个别用于建设之后发送音讯。*
   * @param ctx
   * @throws Exception
   */
  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("client:" + ctx);
    // 给服务器发送音讯
    ctx.writeAndFlush(Unpooled.copiedBuffer("Hello , 服务器", CharsetUtil.UTF_8));
  }

  /**
   * 从服务器接管到一条音讯后被调用
   *
   * @param ctx
   * @param msg
   * @throws Exception
   */
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf byteBuf = (ByteBuf) msg;
    System.out.println("服务器说:" + byteBuf.toString(CharsetUtil.UTF_8));
    System.out.println("服务器地址为:" + ctx.channel().remoteAddress());
  }

  /**
   * 解决异样信息
   *
   * @param ctx
   * @param cause
   * @throws Exception
   */
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();
    ctx.close();}
}

到这里咱们曾经实现了客户端连贯、启动与业务解决逻辑全副过程,尽管很多中央没有介绍,然而不障碍咱们从整体来看 Netty 执行过程,这里疏忽的很多点,后续会一并剖析。

总结

本篇次要想给大家展现 Netty 全貌是何样,只全貌再究细节是我始终比拟推崇的学习思路,这样能够防止一开始局限与某一个细点。

当然本篇埋下了几个坑期待后续的开掘,比方

(1)为啥服务器端须要须要创立两个 NioEventLoopGroup 线程组,而客户端只须要创立一个呢?

(2)为何 handler 都须要继承 Netty 事后设置的 Handler 呢?

后续文章会一并率领大家一起摸索 Netty 的奥秘。

正文完
 0