第一个Netty服务器的代码简析

126 阅读3分钟

前言

在学习Netty过程中我编写了一个极其简单的服务器,实现了一个最基本的功能: 客户端向服务器发送一条消息Hello World!,然后在服务器端显示出来。下面来从运行流程方面做一个简析。

话不多说,先上代码:

Server.java

public class Server{
    public static void main(String[] args){
        // 1
        new ServerBootstrap()
            // 2
            .group(new NioEventLoopGroup())// 15
            // 3
            .channel(NioServerSocketChannel.class)
            // 4
            .childHandler(new ChannelInitializer<NioSocketChannel>() {
                // 11
                @Override
                protected void initChannel(NioSocketChannel ch) {
                    // 16
                    ch.pipeline().addLast(new StringDecoder());//可以添加多个请求处理工序
                    ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
                        // 17
                        @Override
                        protected void channelRead(ChannelHandlerContext ctx, String msg) {
                            System.out.println(msg);
                        }
                    });
                 }
            })
            // 5
            .bind(8080);
    }
}

Client.java

public class{
    public static void main(String[] args){
        // 6
        new Bootstrap()
            // 7
            .group(new NioEventLoopGroup())
            // 8
            .channel(NioSocketChannel.class)
            // 9
            .handler(new ChannelInitializer<Channel>() {
                // 11
                @Override
                protected void initChannel(Channel ch) {
                    // 14
                    ch.pipeline().addLast(new StringEncoder());
                }
            })
            // 10
            .connect("127.0.0.1", 8080)
            // 12
            .sync()
            // 13
            .channel()//channel()返回读写对象,才能发送或接受消息
            .writeAndFlush(new Date() + ": hello world!");
    }
}

首先让我们来理解几个概念

  • 把 channel 理解为数据的通道
  • 把 msg 理解为流动的数据,最开始输入是 ByteBuf,但经过 pipeline 的加工,会变成其它类型对象,最后输出又变成 ByteBuf。数据从客户端到服务器以及服务器到客户端都是以 ByteBuf 形式传输
  • 把 handler 理解为数据的处理器,可以往里面添加很多处理工序(ch.pipeline().addLast()方法)
    • 工序有多道,合在一起就是 pipeline,pipeline 负责发布事件(读、读取完成...)传播给每个 handler, handler 对自己感兴趣的事件进行处理(重写了相应事件处理方法)
    • handler 分 Inbound 和 Outbound 两类
  • 把 eventLoop 理解为处理数据的工人
    • 工人可以管理多个 channel 的 io 操作,并且一旦工人负责了某个 channel,就要负责到底(绑定)
    • 工人既可以执行 io 操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个 channel 的待处理任务,任务分为普通任务、定时任务
    • 工人按照 pipeline 顺序,依次按照 handler 的规划(代码)处理数据,可以为每道工序指定不同的工人

执行流程(按照代码中的注释依次执行)

  1. 启动器,组装 Netty 组件,启动服务器
  2. 创建 NioEventLoopGroup,可以简单理解为 线程池 + Selector 后面会详细展开
  3. 选择服务端 Scoket 实现类,其中 NioServerSocketChannel 表示基于 NIO 的服务器端实现
  4. 添加处理器。注: initChannel 方法不在服务器启动时运行,而是在客户端连接服务器时运行
  5. 绑定监听端口
  6. 启动客户端
  7. 创建客户端 EventLoopGroup
  8. 选择客户端 Socket 实现类
  9. 添加处理器。注: initChannel 方法不在客户端启动时运行,而是在客户端连接服务器时运行
  10. 连接到服务器
  11. 服务器在连接建立后调用 initChannel 初始化方法,客户端同时也调用 initChannel 方法
  12. sync()是一个同步方法,在连接建立完成前一直阻塞,建立完成后结束以继续后面的流程
  13. 向服务器发送数据
  14. Hello World!字符串转为 ByteBuf
  15. 由某个 EventLoop 中的 read() 方法接收 ByteBufg
  16. 将 ByteBuf 还原为字符串
  17. 执行下一个处理工序,将从客户端接收到的字符串打印出来

结语

本文仅记录了一个Netty服务器接收一个请求时的流程,错漏之处,欢迎指正。

一个有梦想的小白的成长之路。欢迎光临个人 gitee首页: Alan_ZhangAoYu