Netty讲解与案例

news2024/10/21 9:22:26
1.Netty简介:

官网:https://netty.io/

Netty 是一个 NIO 客户端服务器框架,可以快速轻松地开发协议服务器和客户端等网络应用程序。它极大地简化和精简了 TCP 和 UDP 套接字服务器等网络编程。

快速简便”并不意味着最终的应用程序会存在可维护性或性能问题。Netty 的设计充分考虑了从实现许多协议(如 FTP、SMTP、HTTP 以及各种二进制和基于文本的旧协议)中获得的经验。因此,Netty 成功地找到了一种无需妥协即可实现易于开发、性能、稳定性和灵活性的方法。

2.特征
2.1 设计
  • 各种传输类型的统一 API - 阻塞和非阻塞套接字
  • 基于灵活且可扩展的事件模型,可以明确分离关注点
  • 高度可定制的线程模型——单线程、一个或多个线程池(如SEDA)
  • 真正的无连接数据报套接字支持(自 3.1 版起)
2.2.易于使用
  • 详尽的 Javadoc、用户指南和示例
  • 无需额外依赖,JDK 5(Netty 3.x)或 6(Netty 4.x)就足够了
    • 注意:某些组件(例如 HTTP/2)可能有更多要求。请参阅 要求页面 了解更多信息
2.3 表现:
  • 更高的吞吐量,更低的延迟
  • 减少资源消耗
  • 最小化不必要的内存复制
2.4 安全
  • 完整的 SSL/TLS 和 StartTLS 支持
3.案例

需求:此案例为模仿C2C聊天案例,类似与IOS和Android进行聊天,通过服务器进行转发给客户端

实在不懂看下图

3.1 maven坐标

创建mave工程,引入坐标依赖

3.2.服务端代码
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class NettyC2CServer {

    // 存储已连接的客户端 <客户端类型, Channel>
    private static Map<String, Channel> clients = new ConcurrentHashMap<>();

    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new StringEncoder());
                            pipeline.addLast(new C2CServerHandler());
                        }
                    });

            System.out.println("Netty C2C Server is starting...");
            ChannelFuture channelFuture = bootstrap.bind(9008).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    // 处理客户端连接与消息
    static class C2CServerHandler extends SimpleChannelInboundHandler<String> {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            // 假设消息格式为 "IDENTITY:客户端类型" 或 "MESSAGE:发送方:接收方:消息内容"
            if (msg.startsWith("IDENTITY")) {
                // 客户端发送标识消息,格式:IDENTITY:android 或 IDENTITY:ios
                String[] parts = msg.split(":", 2);
                if (parts.length == 2) {
                    String clientType = parts[1];
                    // 将客户端连接存储
                    clients.put(clientType, ctx.channel());
                    System.out.println(clientType + " connected.");
                    ctx.channel().writeAndFlush("Server acknowledged: " + clientType);
                } else {
                    ctx.channel().writeAndFlush("Invalid IDENTITY format.");
                }
            } else if (msg.startsWith("MESSAGE")) {
                // 消息格式:MESSAGE:sender:target:message
                String[] parts = msg.split(":", 4);
                if (parts.length == 4) {
                    String sender = parts[1];
                    String target = parts[2];
                    String message = parts[3];

                    System.out.println("Received message from " + sender + ": " + message);

                    // 如果目标客户端在线,发送消息
                    if (clients.containsKey(target)) {
                        clients.get(target).writeAndFlush("Message from " + sender + ": " + message);
                    } else {
                        ctx.channel().writeAndFlush("Target " + target + " is not online.");
                    }
                } else {
                    ctx.channel().writeAndFlush("Invalid MESSAGE format. Use 'MESSAGE:sender:target:message'");
                }
            }
        }

        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            System.out.println("Client connected: " + ctx.channel().id().asShortText());
        }

        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            // 在客户端断开连接时,移除连接
            clients.values().remove(ctx.channel());
            System.out.println("Client disconnected: " + ctx.channel().id().asShortText());
        }
    }
}

bossGroup:负责接受客户端连接

workerGroup:负责处理连接后的读写操作

ChannelInitializer:在每个新的客户端连接时设置管道,添加StringDecoder、StringEncoder用于处理字符串数据,以及自定义的C2CServerHandler来处理消息

handlerAdded

  • 当有新的客户端连接时触发。
  • 打印客户端连接的 ID。

handlerRemoved

  • 当客户端断开连接时触发。
  • clients 中移除该客户端的 Channel
3.3 客户端代码
1.ios代码
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.Scanner;

public class NettyAndroidClient {

    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<Channel>() {
                        //初始化渠道信息
                        @Override
                        protected void initChannel(Channel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new StringEncoder());
                            pipeline.addLast(new SimpleChannelInboundHandler<String>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                                    System.out.println("Received: " + msg);
                                }

                                @Override
                                public void channelActive(ChannelHandlerContext ctx) {
                                    // 客户端连接成功后,发送标识消息
                                    ctx.writeAndFlush("IDENTITY:android");
                                    System.out.println("Android client connected and sent identity.");
                                }
                            });
                        }
                    });

            Channel channel = bootstrap.connect("localhost", 9008).sync().channel();

            Scanner scanner = new Scanner(System.in);

            // 模拟 Android 发送消息给 iOS
            System.out.println("Type your message to send to iOS:");
            while (scanner.hasNextLine()) {
                String message = scanner.nextLine();
                channel.writeAndFlush("MESSAGE:android:ios:" + message);
            }
        } finally {
            group.shutdownGracefully();
        }
    }
}
2.Android代码
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.Scanner;

public class NettyIOSClient {

    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<Channel>() {
                        @Override
                        protected void initChannel(Channel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new StringEncoder());
                            pipeline.addLast(new SimpleChannelInboundHandler<String>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                                    System.out.println("Received: " + msg);
                                }

                                @Override
                                public void channelActive(ChannelHandlerContext ctx) {
                                    // 客户端连接成功后,发送标识消息
                                    ctx.writeAndFlush("IDENTITY:ios");
                                    System.out.println("iOS client connected and sent identity.");
                                }
                            });
                        }
                    });

            Channel channel = bootstrap.connect("localhost", 9008).sync().channel();

            Scanner scanner = new Scanner(System.in);
            
            System.out.println("Type your message to send to Android:");
            while (scanner.hasNextLine()) {
                String message = scanner.nextLine();
                channel.writeAndFlush("MESSAGE:ios:android:" + message);
            }
        } finally {
            group.shutdownGracefully();
        }
    }
}

客户端相同的特点如下解释:

group:用于管理客户端的 I/O 线程。

Bootstrap

  • 用于设置客户端的引导程序。
  • 配置了线程组、通道类型(NioSocketChannel),以及通道初始化逻辑。

ChannelInitializer

  • 在每个新的连接通道上设置管道(pipeline),添加 StringDecoderStringEncoder 处理字符串消息。
  • 添加自定义的 SimpleChannelInboundHandler<String> 处理从服务器接收到的消息。

channelRead0

  • 当客户端接收到来自服务器的消息时,会触发此方法。
  • 打印收到的消息到控制台。

channelActive

  • 当客户端成功连接到服务器时触发。
  • 发送一个标识消息 "IDENTITY:ios/android" 给服务器,告知服务器这是一个 iOS /android客户端。
  • 打印确认信息,表示客户端已连接并发送了身份信息。

案例运行结果:

喜欢的话点个关注,接下来会分享更多知识以及工作感受

需要项目工程私信(这么详细了应该可以写出来的)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2213940.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Halcon 使用二维像素分类对图像进行分割

文章目录 算子histo_2dim 计算双通道灰度值图像的直方图class_2dim_sup 使用二维像素分类对图像进行分割 示例 算子 histo_2dim 计算双通道灰度值图像的直方图 histo_2dim(Regions, ImageCol, ImageRow : Histo2Dim : : )Regions (输入对象)&#xff1a;在此区域内计算直方图…

腾讯云视立方开通各项云服务相关

云直播 如何开通云直播服务&#xff1f; 进入 云直播管理控制台&#xff0c;进入腾讯云直播服务开通页&#xff0c;查看相关协议并勾选同意&#xff0c;单击申请开通即可开通云直播服务。 。 如何开启流防盗链 KEY&#xff1f; 推流防盗链 KEY 是为了确保只有您的 App 用户…

dockerfile 用法全解析

FROM 构建基于alpine的镜像&#xff0c;单条执行就是复制了一个apline镜像(除了FROM其他都是非必须的) WORKDIR 是之指定接下来的shell语句是运行在哪个路径下&#xff0c;没有就会创建目录 COPY 将宿主机指定目录的文件拷贝到镜像指定目录 &#xff08;ADD 源地址还可以url…

[LeetCode] 662. 二叉树最大宽度

题目描述&#xff1a; 给你一棵二叉树的根节点 root &#xff0c;返回树的 最大宽度 。 树的 最大宽度 是所有层中最大的 宽度 。 每一层的 宽度 被定义为该层最左和最右的非空节点&#xff08;即&#xff0c;两个端点&#xff09;之间的长度。将这个二叉树视作与满二叉树结…

【C语言刷力扣】2206.将数组划分成相等数对

题目&#xff1a; 解题思路&#xff1a; 题目中要求元素成数对出现&#xff0c;即每个元素出现偶数次。用哈希表存放每个数出现的次数&#xff0c;再循环查看每个数的次数是否位偶数。 typedef struct {int key;int count;UT_hash_handle hh; } hashEntry;bool divideArray(int…

IDEA下载安装

文章目录 1、下载安装包2、安装IDEA3、全局配置4、安装插件 1、下载安装包 IDEA官网下载最新IDEA。 上面的ULtimate是旗舰版&#xff0c;试用30天&#xff0c;之后是需要收费的&#xff0c;下面黑色区域的Community是社区版&#xff0c;功能不如旗舰版丰富&#xff0c;但是好在…

文件的二维码怎么做成?简单的3步生成二维码技巧

数字化时代的到来&#xff0c;用来将内容分享给其他人展示的方式也越来越多。其中二维码就是现在很流行的一种方式&#xff0c;将内容存入二维码后&#xff0c;其他人就可以扫描分享二维码来查看内容&#xff0c;那么文件生成二维码该怎么操作呢&#xff1f; 通过使用文件二维…

如何轻松实现Patreon订阅,获得独家内容

目录 1. 什么是Patreon&#xff1f; Patreon是一个非常流行的在线平台。 它支持内容创作者通过订阅的方式,直接从粉丝那里获取资金支持。无论是艺术家、音乐家,还是作家、视频制作人,都能在这里找到自己的粉丝群体。 简而言之,你可以通过订阅他们定期发布的独家内容,享受到更…

量子噪声与量子操作

由于量子不确定性和量子态的测量过程而引入的随机波动&#xff0c;量子噪声不可避免。 经典噪声 想象一下&#xff0c;一个比特存储在硬盘驱动器上&#xff0c; 它与普通计算机相连&#xff0c;该比特从状态0或1开始&#xff0c;经过长时间&#xff0c;散乱的磁场很可能会导致…

020 elasticsearch7.10.2 elasticsearch-head kibana安装

文章目录 全文检索流程ElasticSearch介绍ElasticSearch应用场景elasticsearch安装允许远程访问设置vm.max_map_count 的值 elasticsearch-head允许跨域 kibana 商品数量超千万&#xff0c;数据库无法使用索引 如何使用全文检索&#xff1a; 使用lucene&#xff0c;在java中唯一…

AI视频监控卫士:一键Docker简易安装,开源技术引领视频监控

AI视频监控卫士的主要应用场景&#xff1a; 我们决定开源的原因&#xff1a; 1. 灵活性与可定制性&#xff1a; 开源产品的代码对用户公开&#xff0c;允许开发者根据特定需求进行自定义和扩展。思通数科AI视频监控卫士作为开源项目&#xff0c;可以灵活适应不同企业或项目的需…

一些NLP代表性模型

&#xff08;一&#xff09;BERT 由Bidirectional Encoder Representations from Transformers的首字母组成&#xff0c;是encoder-only结构类型的代表。 模型分预训练和微调两步&#xff0c;预训练任务有两类&#xff1a;masked language model(MLM)、next sentence predict…

我作为TypeScript开发人员是如何学习Rust的

讲讲我作为TypeScript开发人员学习Rust的经验吧&#xff0c;希望对你有帮助 像许多开发人员一样&#xff0c;我通过专注于网络技术开始了我的编程生涯。我相信这是一个很好的起点&#xff0c;JavaScript&#xff0c;互联网的语言&#xff0c;以及许多&#xff0c;是一个令人难…

Axure原型设计秘籍:解锁高效设计与开发的宝藏工具

​ 在快速迭代的数字时代&#xff0c;原型设计成为了产品开发与用户体验优化的关键环节。Axure&#xff0c;作为一款备受推崇的原型设计工具&#xff0c;凭借其强大的功能、直观的界面和丰富的资源&#xff0c;成为了设计师和产品经理们的得力助手。本文将带你深入探索Axure的原…

JavaScript中的对象,常用内置对象和数据类型

一、对象 1.概念 什么是对象&#xff1f; 在JavaScript中&#xff0c;对象是一组无序的相关属性和方法集合&#xff0c;所有的事物都是对象&#xff0c;例如&#xff1a;字符串、数值、数组和函数等等。 对象是由属性和方法组成的。 属性&#xff1a;事物的特征&#xff0c;在…

使用CSS Flexbox创建简洁时间轴

使用CSS Flexbox创建简洁时间轴 在网页设计中,时间轴是一种常见且有效的方式来展示事件的顺序和进程。本文将介绍如何使用CSS Flexbox创建一个简洁优雅的时间轴,无需复杂的JavaScript代码。 基本HTML结构 首先,我们需要创建基本的HTML结构: html复制<div class"ti…

《CUDA编程》8.共享内存的合理使用

共享内存是 一种可被程序员直接操控的缓存&#xff0c;主要作用有两个&#xff1a; ①减少核函数中对全局内存的访 问次数&#xff0c;实现高效的线程块内部的通信 ②提高全局内存访问的合并度 将通过两个具体的例子阐明共享内存的合理使用&#xff0c;一个数组归约的例子和讨矩…

深入解析“低代码平台”与“零代码平台”:区别与应用场景

随着企业数字化转型的加速&#xff0c;低代码平台与零代码平台作为提升开发效率、降低IT成本的有效工具&#xff0c;正逐渐成为企业软件开发的新趋势。这两种平台虽然都旨在简化应用开发过程&#xff0c;但在功能、适用场景及用户群体上存在着显著差异。 低代码平台&#xff1a…

如何通过Chrome设置保护你的在线隐私

在当今数字时代&#xff0c;保护个人隐私和在线安全变得尤为重要。谷歌浏览器作为全球最受欢迎的网络浏览器之一&#xff0c;提供了多种功能来帮助用户保护自己的在线隐私。本教程将指导你如何通过谷歌浏览器设置来提高你的在线隐私保护水平。&#xff08;本文由https://www.li…