• [AIGC] 快速掌握Netty,打造高性能IM服务器!


    前言:Netty 是一个非常优秀的网络应用程序框架,支持高并发、高性能的网络通信,适用于开发各种服务器程序,如即时通讯、游戏、物联网等。使用 Netty 可以大大提升服务器程序的性能和可靠性。本文将介绍 Netty 的基本原理和使用方法,并给出一个简单的 IM 程序示例,帮助读者快速掌握 Netty 的使用。


    一、什么是Netty?

    Netty是一个高性能、事件驱动的网络应用程序框架,基于 Java NIO (Non-blocking I/O) 开发。它主要用于开发高性能的服务器程序,例如即时通讯、游戏、物联网等。

    二、为什么使用 Netty?

    使用 Netty 的主要原因包括:

    1. 高性能:基于 Java NIO 实现,支持高并发,I/O 处理能力强,同时在可用性方面有一定的优化。

    2. 高可靠性:充分利用 Java NIO 机制,避免 I/O 阻塞,提高应用程序的可用性。

    3. 易于开发:提供了丰富的 API 和开发文档,API 使用简单易懂,易于上手。

    4. 可扩展性强:可自定义编解码器、拦截器、消息处理器等,可以很方便地扩展新的协议。

    5. 支持多种协议:支持 TCP/UDP、HTTP、WebSocket 等多种协议。

    三、怎么使用 Netty?

    使用 Netty 主要需要进行以下几个步骤:

    1. 创建服务器启动类,配置服务器的参数和启动程序。

    2. 编写客户端连接请求处理类,处理客户端连接请求,建立连接。

    3. 编写消息处理类,对客户端发送的消息进行解码和处理。

    4. 编写消息编码类,将服务器的响应消息编码成二进制数据。

    5. 实现客户端和服务器之间的数据交互,完成数据的发送和接收。

    四、Netty 实现一个IM程序

    以下是一个使用 Netty 实现的简单的 IM 程序,支持用户登录、发送消息、退出的功能。

    public class NettyIMServer {
    
        public static void main(String[] args) throws Exception {
            // 创建 ServerBootstrap
            ServerBootstrap bootstrap = new ServerBootstrap();
    
            // 配置 EventLoopGroup
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
    
            try {
                bootstrap.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ChannelPipeline pipeline = ch.pipeline();
    
                                // 解码器
                                pipeline.addLast(new StringDecoder());
                                // 编码器
                                pipeline.addLast(new StringEncoder());
                                // 消息处理器
                                pipeline.addLast(new NettyIMServerHandler());
                            }
                        });
    
                // 绑定端口
                ChannelFuture future = bootstrap.bind(8888).sync();
                System.out.println("Server started and listen on: " + future.channel().localAddress());
                // 等待服务器监听端口关闭
                future.channel().closeFuture().sync();
            } finally {
                // 释放资源
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    
        static class NettyIMServerHandler extends SimpleChannelInboundHandler<String> {
    
            private static final Map<String, Channel> clients = new ConcurrentHashMap<>();
    
            @Override
            protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                // 获取 Channel
                Channel channel = ctx.channel();
                // 判断消息类型
                if (msg.startsWith("[Login]")) {
                    // 登录消息,获取用户名
                    String username = msg.substring(7);
                    System.out.println("用户[" + username + "]登录了");
                    // 将用户信息保存到 clients map 中
                    clients.put(username, channel);
                } else if (msg.startsWith("[Logout]")) {
                    // 退出消息,获取用户名
                    String username = msg.substring(8);
                    System.out.println("用户[" + username + "]退出了");
                    // 将用户信息从 clients map 中移除
                    clients.remove(username);
                } else if (msg.startsWith("[SendTo]")) {
                    // 发送消息,格式为 [SendTo]:[username]:[message]
                    String[] parts = msg.split(":", 3);
                    String to = parts[1];
                    String message = parts[2];
                    // 获取发送者用户名
                    String from = clients.entrySet().stream()
                            .filter(entry -> channel.equals(entry.getValue()))
                            .map(Map.Entry::getKey)
                            .findFirst()
                            .orElse("unknown");
                    // 查找接收者
                    Channel toChannel = clients.get(to);
                    if (toChannel != null) {
                        // 发送消息给接收者
                        toChannel.writeAndFlush("[" + from + "]: " + message + "\n");
                    } else {
                        // 接收者不存在,发送错误消息给发送者
                        channel.writeAndFlush("[Error]: User not found\n");
                    }
                }
            }
    
            @Override
            public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
                System.out.println("客户端[" + ctx.channel().remoteAddress() + "]连接成功");
                super.handlerAdded(ctx);
            }
    
            @Override
            public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
                System.out.println("客户端[" + ctx.channel().remoteAddress() + "]断开连接");
                super.handlerRemoved(ctx);
            }
    
            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                System.out.println("客户端[" + ctx.channel().remoteAddress() + "]发生异常:" + cause.getMessage());
                super.exceptionCaught(ctx, cause);
            }
        }
    }
    
    • 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
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102

    这段程序是一个基于 Netty 的简单的 IM(即时通讯)服务器程序,它可以实现用户登录、退出和发送消息等功能。

    程序主要分为两部分,第一部分是服务器的启动,第二部分是消息处理器。

    在服务器启动部分,程序首先创建了一个 ServerBootstrap 实例,该实例用于启动服务器。然后配置了两个 EventLoopGroup 实例,一个用于接收客户端连接,一个用于处理客户端请求。接着使用 bootstrap.group() 方法将两个 EventLoopGroup 实例关联起来,并指定服务器的 Channel 类型为 NioServerSocketChannel。最后使用 childHandler() 方法设置消息的处理器。

    在消息处理器部分,程序首先定义了一个 Map 类型的 clients 对象,用于保存客户端的信息。然后重写了 channelRead0() 方法,该方法会在客户端发送消息时被调用。在该方法中,程序根据消息内容判断消息类型,并执行相应的逻辑。

    如果是登录消息,则从消息中提取出用户名,并将其保存到 clients 对象中;如果是退出消息,则从消息中提取出用户名,并从 clients 对象中移除该用户;如果是发送消息,则从消息中提取出接收者和消息内容,并通过 clients 对象查找接收者的 Channel,然后将消息发送给接收者。在发送消息时,程序还会将发送者的用户名添加到消息前面,以便接收者知道消息是由谁发送的。

    此外,程序还重写了 handlerAdded()、handlerRemoved() 和 exceptionCaught() 方法,用于处理客户端连接和异常。在 handlerAdded() 方法中,程序会记录客户端连接成功的事件;在 handlerRemoved() 方法中,程序会记录客户端断开连接的事件;在 exceptionCaught() 方法中,程序会记录客户端发生异常的事件。

    总结

    Netty 是一个高性能、事件驱动的网络应用程序框架,可以用于开发高性能的服务器程序,例如即时通讯、游戏、物联网等。使用 Netty 可以获得高性能、高可靠性、易于开发、可扩展性强、支持多种协议的优点。

    使用 Netty 主要需要进行以下几个步骤:创建服务器启动类,配置服务器的参数和启动程序;编写客户端连接请求处理类,处理客户端连接请求,建立连接;编写消息处理类,对客户端发送的消息进行解码和处理;编写消息编码类,将服务器的响应消息编码成二进制数据;实现客户端和服务器之间的数据交互,完成数据的发送和接收。

    本文还介绍了一个使用 Netty 实现的简单的 IM 程序,支持用户登录、发送消息、退出的功能。该程序主要分为服务器的启动和消息处理器两部分,可以作为学习 Netty 和 IM 服务器的入门示例。

  • 相关阅读:
    神经网络 设计层数和神经元数量的考虑
    【文本分类】《融合注意力和剪裁机制的通用文本分类模型》
    uni-app实现拍照功能
    【Elixr】 introduce
    K_A07_002 基于 STM32等单片机驱动ULN2003模块按键控制步进电机正反转
    VL53L5CX驱动开发(1)----驱动TOF进行区域检测
    记录SpringCloud使用Zookeeper做服务中心遇到的问题
    JDBC与Spring事务及事务传播性原理解析-下篇
    MySQL实现的一点总结(三)
    uview的安装和功能
  • 原文地址:https://blog.csdn.net/2301_79528833/article/details/133445504