Skip to content

Netty上手

1. 目标

开发一个简单的服务器端和客户端:

  • 客户端向服务器端发送hello, world
  • 服务器仅接收,不返回

2. 工程准备

在idea中创建netty-learn的maven工程, 添加如下依赖:

xml
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.119.Final</version>
</dependency>

3. 服务器端代码实现

java
public class NettyServerDemo1 {
    public static void main(String[] args) {
        // 1.就是Netty的启动器对象,负责组装 netty各个组件,启动服务器
        new ServerBootstrap()
                // 2. 所谓EventLoop就是由selector+thread的组成, group表示可以有多个selector+thread
                // 用于处理可连接事件,所谓的boss线程
                .group(new NioEventLoopGroup())
                /** 3. ServerSocketChannel的具体实现选择配置, 具体的实现有如下:
                 *   NioServerSocketChannel是通用的NIO,
                 *   EpollServerSocketChannel,用于linux平台
                 *   KQueueServerSocketChannel 用于mac平台
                 *   OioServerSocketChannel 通用的BIO
                 */
                .channel(NioServerSocketChannel.class)
                // 4. 负责读写操作,所谓childHandler就是worker具体操作
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    // 5. 固定格式,在initChannel里面添加具体的bytebuffer处理类
                    protected void initChannel(NioSocketChannel ch) {
                        ch.pipeline().addLast(new StringDecoder());
                        // 6. 可以添加多个处理类,这里使用自定义的处理类,这里用来打印
                        ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
                            @Override
                            protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                                System.out.println(msg);
                            }
                        });
                    }
                })
                // 7. 绑定端口
                .bind(8080);
    }
}

在位置3处,选择服务Scoket实现类,其中NioServerSocketChannel表示基于NIO的服务器端实现,其它实现还有:
Alt text 在位置4处,为啥方法叫childHandler,是接下来添加的处理器都是给SocketChannel用的,而不是给 ServerSocketChannel。ChannelInitializer处理器(仅执行一次),它的作用是待客户端SocketChannel建立连接后,执行initChannel以便添加更多的处理器。

4. 客户端代码实现

java
public class NettyClientDemo1 {
    public static void main(String[] args) throws InterruptedException {
        // 1。 就是Netty的启动器对象,负责组装 netty各个组件,启动客户端
        new Bootstrap()
                // 2. 所谓EventLoop就是由selector+thread的组成, group表示可以有多个selector+thread
                // 用于处理可连接事件,所谓的boss线程
                .group(new NioEventLoopGroup())
                /** 3. SocketChannel的具体实现选择配置, 具体的实现有如下:
                 *   NioSocketChannel是通用的NIO,
                 *   EpollSocketChannel,用于linux平台
                 *   KQueueSocketChannel 用于mac平台
                 *   OioSocketChannel 通用的BIO
                 */
                .channel(NioSocketChannel.class)
                // 4. 负责读写操作,所谓handler就是worker具体操作
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    // 5. 固定格式,连接后被调用
                    protected void initChannel(NioSocketChannel ch) {
                        // 7. 将字符串转成byte
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                // 8. 连接服务器
                .connect(new InetSocketAddress("localhost", 8080))
                // 9. 阻塞方法,直到连接建立
                .sync()
                // 10. 向服务器发送数据
                .channel().writeAndFlush("hello world");
    }
}

在位置3处,选择客户SocketChannel实现类,NioSocketChannel表示基于NIO的客户端实现,其它实现还有 Alt text 在位置9处,Netty中很多方法都是异步的,如connect,这时需要使用sync()方法等待connect建立连接完毕。
在位置10处,获取channel对象,它即为通道抽象,可以进行数据读写操作。

💡提示

一开始需要树立正确的观念

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