Netty 应用之Hello World

实现Netty Hello World 步骤

首先导入netty得依赖是必须的,我这次的helloworld只自定义客户端和服务端的处理器,引入ChannelInboundHandlerAdapter入栈handler适配器,简单业务部分,服务端会触发对于的生命周期方法,并打印到控制台,还会响应客户端一些信息,就这么多了,开始上代码。

1、导入netty依赖

 <dependency>
     <groupId>io.netty</groupId>
     <artifactId>netty-all</artifactId>
     <version>4.1.66.Final</version> <!-- 请替换为最新版本 -->
 </dependency>

2、服务端代码展示

创建一个NettyServer 的类
主要部分有创建两个事件循环组,bossLoopGroup 和workerLoopGroup ,Netty使用EventLoopGroup来管理EventLoop线程
还需要创建一个服务端的启动对象ServerBootstrap ,这是一个链式编程的类,可以设置参数 通道类型、服务端处理器类型、连接客户端处理器类型
netty服务端主要由以下几个核心组件构成
Netty使用EventLoopGroup来管理EventLoop线程
Channel是Netty中的网络通信组件
Handler是Netty中处理网络事件的逻辑组件
Bootstrap是Netty中用于初始化和配置客户端或服务器端的启动类。
最后返回Future,Netty的IO操作是异步的,返回Future对象,开发者可以通过Future来获取操作的结果。

public class NettyServer {
    public static void main(String[] args) {
        // 创建Reactor
        // 用来管理channel 监听事件 ,是无限循环的事件组(线程池)
        EventLoopGroup bossLoopGroup = new NioEventLoopGroup();
        EventLoopGroup workerLoopGroup = new NioEventLoopGroup();
        // 服务端的启动对象
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 设置相关参数  这是一个链式编程
        serverBootstrap.group(bossLoopGroup,workerLoopGroup)
                // 声明通道类型
                .channel(NioServerSocketChannel.class)
                // 设置处理器  我这里设置了netty提供的Handler 处理器
                .handler(new LoggingHandler(LogLevel.INFO))
                // 定义客户连接端处理器的使用
                // ChannelInitializer 通道处理化
                // 可以自定义通道初始化器,如实现编码解码器时
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 需要处理的是客户端通道
                        // 通道代表的是 连接的角色 管道代表的是 处理业务的逻辑管理
                        // 管道相当与一个链表, 将不同的处理器连接起来,管理的是处理器的顺序
                        ch.pipeline().addLast(new NettyServerHandler());

                    }
                });

        System.out.println("服务端初始化完成");

        // 启动需要设置端口  还需要设置是异步启动
        try {
            // 设置异步的future
            ChannelFuture future = serverBootstrap.bind(8888).sync();


            // 将关闭的通道也设置成异步的
            // 阻塞finally 中的代码
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            // 优雅关闭
            bossLoopGroup.shutdownGracefully();
            workerLoopGroup.shutdownGracefully();
        }


    }
}

细分步骤

(1) 创建主从事件循环组

// 创建Reactor
// 用来管理channel 监听事件 ,是无限循环的事件组(线程池)
EventLoopGroup bossLoopGroup = new NioEventLoopGroup();
EventLoopGroup workerLoopGroup = new NioEventLoopGroup();
 

(2) 创建服务启动对象

        // 服务端的启动对象
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 设置相关参数  这是一个链式编程
        serverBootstrap.group(bossLoopGroup,workerLoopGroup)
                // 声明通道类型
                .channel(NioServerSocketChannel.class)
                // 设置处理器  我这里设置了netty提供的Handler 处理器
                .handler(new LoggingHandler(LogLevel.INFO))
                // 定义客户连接端处理器的使用
                // ChannelInitializer 通道处理化
                // 可以自定义通道初始化器,如实现编码解码器时
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 需要处理的是客户端通道
                        // 通道代表的是 连接的角色 管道代表的是 处理业务的逻辑管理
                        // 管道相当与一个链表, 将不同的处理器连接起来,管理的是处理器的顺序
                        ch.pipeline().addLast(new NettyServerHandler());

                    }
                });

(3) 设置服务器启动端口和关闭线程池也就是事件循环组

        // 启动需要设置端口  还需要设置是异步启动
        try {
            // 设置异步的future
            ChannelFuture future = serverBootstrap.bind(8888).sync();


            // 将关闭的通道也设置成异步的
            // 阻塞finally 中的代码
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            // 优雅关闭
            bossLoopGroup.shutdownGracefully();
            workerLoopGroup.shutdownGracefully();
        }

自定义处理器

上面的NettyServerHandler类处理器下面定义了
需要继承ChannelInboundHandlerAdapter 还注意到还有一个SimpleChannelInboundHandler的子类,也可以直接继承,也能达到相同的目的。
handler的逻辑如下
a) 继承ChannelInboundHandlerAdapter, 此为netty提供的适配器
b) 重写其中的方法,channelActive 、channelRead、channelReadComplete,分别对应于通道创建、读事件发生、读事件完成三个时间点。
c) 方法的参数有一个 ChannelHandlerContext ,是处理器的上下文,除了获取通道和管道外,可以调用writeAndFlush() 直接写入数据

在这里插入图片描述

ChannelInboundHandlerAdapter 父子继承关系图

// 基础 ChannelInboundHandlerAdapter 入栈handler 相当于适配器
// 提供 不同时期使用的方法  ctrl + O 实现父类方法
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    //  通道被启用 刚刚建立连接 的时候触发
    // 业务使用一般 发送欢迎消息

    /**
     *
     * @param ctx 通道处理器上下文
     * @throws Exception
     * 通道被启用 刚刚建立连接 的时候触发
     * 可以整合使用过程中需要的参数 如:  通道channel  管道pipeline
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 直接写入字符串 底层还是获取通道--建立缓冲区 ---写入数据---缓冲区写入通道
        System.out.println("channelActive done");
        ctx.writeAndFlush("Welcome to Netty Server");
    }

    /**
     *
     * @param ctx  通道处理器上下文
     * @param msg 读取的消息数据
     * @throws Exception
     * 读取消息时触发的方法
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf)msg;
        System.out.println("客户端消息:"+buf.toString(CharsetUtil.UTF_8));
        // 获取客户端地址
        System.out.println("客户端地址:"+ctx.channel().remoteAddress());
        // 可以使用引用计数工具类ReferenceCountUtil的方式来释放ByteBuf
        // 后面后使用SimpleChannelInboundHandler.channelRead0()方法
        ReferenceCountUtil.release(msg);

    }

    /**
     *
     * @param ctx 通道处理器上下文
     * @throws Exception
     * 数据读取完成时触发的事件
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // Unpooled是 ByteBuf 和 String 之间方便转换的工具
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello client ",CharsetUtil.UTF_8));
    }
}

3、客户端代码展示

客户端代码就相对比较简单,和服务器代码差不多,就些许不同比如事件循环组只需要一个就行

创建一个NettyClient 类


public class NettyClient {

    public static void main(String[] args) {

        // 客户端事件循环组 只需要一个
        EventLoopGroup group = new NioEventLoopGroup();
        // 客户端启动器
        Bootstrap bootstrap = new Bootstrap();

        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new NettyClientHandler());

                    }
                });

        System.out.println("客户端初始化完成了");
        try {
            // 设置异步的future  需要连接的服务端参数
            ChannelFuture future = bootstrap.connect("127.0.0.1", 8888).sync();
            // 将关闭的通道也设置成异步的
            // 阻塞finally 中的代码
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
    }
}

同样的配方客户端自定义了处理器NettyClientHandler,接收服务端信息和响应服务端


public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    /**
     *
     * @param ctx 通道处理器上下文
     * @throws Exception
     * 通道被启用 刚刚建立连接 的时候触发
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        System.out.println(" client channelActive done ");
        ctx.writeAndFlush(Unpooled.copiedBuffer("你好服务端,我是客户端", CharsetUtil.UTF_8));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        System.out.println(" client channelRead done ");
        ByteBuf buf = (ByteBuf)msg;
        System.out.println("server msg:"+buf.toString(CharsetUtil.UTF_8));
        System.out.println("服务端地址:"+ctx.channel().remoteAddress());


    }
}

结语:本文中主要是本人通过看视频学习总结的,有错误的地方,还请指出。

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-10 14:36:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-10 14:36:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-10 14:36:04       82 阅读
  4. Python语言-面向对象

    2024-04-10 14:36:04       91 阅读

热门阅读

  1. 细说golang语法糖

    2024-04-10 14:36:04       32 阅读
  2. php根据用户地址获取经纬度

    2024-04-10 14:36:04       33 阅读
  3. 深入剖析webrtc事件机制 sigslot

    2024-04-10 14:36:04       28 阅读
  4. rust学习(recursive mutex 实现)

    2024-04-10 14:36:04       34 阅读
  5. pytorch进阶

    2024-04-10 14:36:04       36 阅读
  6. leetcode2529--正整数和负整数的最大计数

    2024-04-10 14:36:04       35 阅读
  7. vs2019不能创建控制台程序

    2024-04-10 14:36:04       28 阅读
  8. LeetCode热题100

    2024-04-10 14:36:04       33 阅读
  9. 联邦学习中的差分隐私与同态加密

    2024-04-10 14:36:04       33 阅读
  10. 渗透工具及其知识库(个人笔记)

    2024-04-10 14:36:04       33 阅读