Netty学习 应用Demo之“自动回复”聊天业务

Netty实现自动回复步骤

主要分成五步
1、创建EventLoopGroup实现循环组 管理EventLoop线程
2、创建Bootstrap ,Bootstrap对于服务端而言,先后设置其中的线程组group、通道channel、处理器handler、客户端通道对应的处理器childHandler
3、自定义服务器接收和响应客户端处理器和初始化器
4、返回Future对象,通过Future来获取操作的结果,比如:将关闭的通道也设置成异步的
5、优雅的关闭事件循环组

这里我说明一下我觉得很重要 ChannelHandlerContext通道处理器上下文 、ChannelPipeline 管道、Hander处理器的关系,下面的一行代码可以说明
pipeline.addLast(new XXXHandler());其实pipeline添加的是ChannelHandlerContext 包裹的Hander 就像这样 new ChannelHandlerContext (new XXXHandler()) ,这个对象添加到了pipeline

1、服务端代码

创建MyChatServer 类作为服务端

public class MyChatServer {public static void main(String[] args) {// 创建Reactor// 用来管理channel 监听事件 ,是无限循环的事件组(线程池) 可以设置线程数,cup核 * numEventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);EventLoopGroup workerLoopGroup = new NioEventLoopGroup();// 服务端的启动对象ServerBootstrap serverBootstrap = new ServerBootstrap();// 设置相关参数  这是一个链式编程serverBootstrap.group(bossLoopGroup,workerLoopGroup)// 声明通道类型.channel(NioServerSocketChannel.class)// 设置处理器  我这里设置了netty提供的Handler 处理器.handler(new LoggingHandler(LogLevel.INFO))// 当连接被阻塞 ,SO_BACKLOG 阻塞队列的长度.option(ChannelOption.SO_BACKLOG,128)// 设置连接保持活跃的状态.childOption(ChannelOption.SO_KEEPALIVE,true).childHandler(new MyChatServerInitializer());System.out.println("服务端初始化完成");// 启动需要设置端口  还需要设置是异步启动try {// 设置异步的futureChannelFuture future = serverBootstrap.bind(8899).sync();// 将关闭的通道也设置成异步的// 阻塞finally 中的代码future.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();}finally {// 优雅关闭bossLoopGroup.shutdownGracefully();workerLoopGroup.shutdownGracefully();}}
}

1.1 自定义初始化器

下面是自定义初始化器的代码,自定义handler的设置,需要先有通道初始化器ChannelInitializer,实现其中的通道初始化方法,具体逻辑为 获取通道中的管道,然后加入handler

在这里插入图片描述

ChannelInitializer继承关系图

public class MyChatServerInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {// 创建管道ChannelPipeline pipeline = socketChannel.pipeline();// 先设置解码器 后设置编码器// 基于lineDelimiter 分隔符的一种解码器pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));// 设置处理器  pipeline 管理的是由handlerContext包裹的handle   add[handlerContext(hander)]// pipeline 是双向链表 headContext头节点   tailContext尾节点pipeline.addLast(new MyChatServerHandler());}
}

1.2 自定义handler处理器

handler的逻辑如下
a) 继承SimpleChannelInboundHandler,还可以设置泛型对应需要处理的msg的类型是除继承适配器之外自定义处理器的另一种方法。
b) 重写其中的方法,channelActive 、channelRead、channelReadComplete,分别对应于通道创建、读事件发生、读事件完成三个时间点。
c) 方法的参数有一个 ChannelHandlerContext ,是处理器的上下文,除了获取通道和管道外,可以调用writeAndFlush() 直接写入数据。


/*** 自定义处理器的另一种方法* SimpleChannelInboundHandler 是 ChannelInboundHandlerAdapter的子类*/
public class MyChatServerHandler extends SimpleChannelInboundHandler {// 当多个通道传入handler , 使用通道组的管理方法// GlobalEventExecutor 全局事件执行器//INSTANCE 代表的是单例private  static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);/*** channelRead0 处理读取数据逻辑的方法** @param ctx  通道处理器上下文  它是连接pipeline 和 handler 中间角色* @param msg* @throws Exception** 读取数据, 并广播给其它客户端* 步骤:* 接收自身的Channel* 这个方法可以帮我们自动释放ByteBuf的内存空间*/protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("[客户端]"+msg);Channel selfChannel = ctx.channel();//提前处理一些消息
//        ByteBuf buf = (ByteBuf)msg;// 迭代channel通道组Iterator<Channel> it = channelGroup.iterator();while (it.hasNext()){Channel ch = it.next();if(selfChannel != ch){ // 如果这个通道不是自身通道 则ch.writeAndFlush("[服务器] - "+selfChannel.remoteAddress()+"发送的消息:"+msg);continue;}// 如果是自身的通道 【客户端发给服务端,服务端回应客户端的消息】String answer ;// 如果是发送消息的通道if (((String)msg).length() == 0 ){answer = "期待你说点什么\r\n";}else {answer = "你说的是这句话嘛:" + msg + "?\r\n";}ch.writeAndFlush(answer);}}/***  连接成功, 此时通道是活跃的时候触发* @param ctx* @throws Exception*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {LocalDate today = LocalDate.now();String dateStr = today.toString(); // 默认格式为 "yyyy-MM-dd"ctx.writeAndFlush("Welcome to server-- now :"+dateStr+"\r\n");}/***  通道不活跃 ,用于处理用户下线的逻辑* @param ctx* @throws Exception*/@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {System.out.println(ctx.channel().remoteAddress()+"下线了\r\n");}/**** @param ctx 通道处理器上下文* @throws Exception* 连接刚刚建立时 ,第一个被执行的方法,*/@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {System.out.println("[服务端地址]:"+ctx.channel().remoteAddress()+"连接成功\r\n");// 添加到通道组中管理channelGroup.add(ctx.channel());}/**** @param ctx  通道处理器上下文* @throws Exception* 当连接断开 最后执行的方法* 连接断开时 , channel 会自动从 通道组中移除*/@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {System.out.println("[服务端地址]:"+ctx.channel().remoteAddress()+"断开连接\r\n");}/***  通用异常处理类* @param ctx 通道处理器上下文* @param cause* @throws Exception*/@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {// 关闭ctx.close();}
}

2、客户端代码

代码和服务端代码变化不大。直接上代码看看
不同的是,客户端事件循环组需要一个,同样也需要自定义初始化器和自定义处理器


public class MyChatClient {public static void main(String[] args) {// 客户端事件循环组 只需要一个EventLoopGroup group = new NioEventLoopGroup();// 客户端启动器Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioSocketChannel.class)// 自定义通道初始化器.handler(new MyChatClientInitializer());System.out.println("客户端初始化完成了");try {// 设置异步的future  需要连接的服务端参数ChannelFuture future = bootstrap.connect("127.0.0.1", 8899).sync();// 将关闭的通道也设置成异步的// 阻塞finally 中的代码
//            future.channel().closeFuture().sync();// 因为 本次业务是可以多次发送消息的业务, 所以上面的阻塞关闭 需要注释掉// 通过键盘输入发送的数据  BufferedReader 来实现BufferedReader br = new BufferedReader(new InputStreamReader(System.in));for (;;) {String msg = br.readLine();// 发送到通道之中future.channel().writeAndFlush(msg+"\r\n");}} catch (Exception e) {e.printStackTrace();}finally {group.shutdownGracefully();}}
}

2.1 自定义初始化器

这个部分引入了Netty 编解码器
DelimiterBasedFrameDecoder 分隔符的一种解码器
StringEncoder / StringDecoder 对字符串处理
ObjectEncoder / ObjectDecoder 对java对象处理
最后还需要一个自定义的处理器MyChatClientHandler 后面有代码


public class MyChatClientInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();// 先设置解码器 后设置编码器// 基于lineDelimiter 分隔符的一种解码器pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));// 添加处理器pipeline.addLast(new MyChatClientHandler());}
}

2.2 自定义处理器

/*** SimpleChannelInboundHandler*  客户端需要添加 msg 泛型*/
public class MyChatClientHandler extends SimpleChannelInboundHandler<String> {/**** @param ctx* @param msg* @throws Exception*/@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {// 接收服务的消息System.out.println("[服务端]:"+msg);}
}

最后展示

业务:自动回应客户端是否说的是msg ,如果直接回车回应“期待你说点什么”
在这里插入图片描述

程序运行结果

结语:希望可以给@你带来一点点帮助,资料来源于网上学习视频总结。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#操作MySQL从入门到精通(6)——对查询数据进行排序

前言 在和MySql数据库交互的过程中,查询数据是使用最频繁的操作,并且我们经常需要对查询到的数据进行排序后输出,比如我想查询1列数据的最小值,那么我可以将查询到的数据进行升序(从小到大)排列,然后取第一个数据就是最小值。本文详细介绍了对查询数据进行排序的各种操…

HarmonyOS4-Stage模型

Stage模型介绍【舞台模型】&#xff1a; Stage模型 应用配置文件 全局应用配置文件&#xff1a; 模块配置文件&#xff1a; Ability生命周期 页面及组件的生命周期&#xff1a; 启动模式&#xff1a; "launchType": "multiton" // 会重新建&#xff0c…

本地项目提交 Github

工具 GitIdeaGithub 账号 步骤 使用注册好的 Github 账号&#xff0c;登陆 Github&#xff1b; 创建 Repositories (存储库)&#xff0c;注意填写图上的红框标注&#xff1b; 创建完成之后&#xff0c;找到存储库的 ssh 地址或 https 地址&#xff0c;这取决于你自己的配置…

matlab:有限差分求解纳维尔(Navier)边界的双调和(Biharmonic)方程,边值为零

我们考虑如下形式的双调和方程的数值解 其中&#xff0c;Ω是欧氏空间中的多边形或多面体域&#xff0c;在其中&#xff0c;d为维度&#xff0c;具有分段利普希茨边界&#xff0c;满足内部锥条件&#xff0c;f(x) ∈ L2(Ω)是给定的函数&#xff0c;∆是标准的拉普拉斯算子。算…

javaScript手写专题——实现instanceof/call/apply/bind/new的过程/继承方式

目录 原型链相关 手写instanceof 实现一个_instance方法&#xff0c;判断对象obj是否是target的实例 测试 手写new的过程 实现一个myNew方法&#xff0c;接收一个构造函数以及构造函数的参数&#xff0c;返回构造函数创建的实例对象 测试myNew方法 手写类的继承 ES6&…

【单片机】PMS5003,PM2.5传感器数据读取处理

文章目录 传感器介绍数据处理解析pm2.5的代码帮助、问询 传感器介绍 PMS5003是一款基于激光散射原理的数字式通用颗粒物浓度传感器,可连续采集 并计算单位体积内空气中不同粒径的悬浮颗粒物个数,即颗粒物浓度分布,进而 换算成为质量浓度,并以通用数字接口形式输出。本传感器可…

3D Web轻量化引擎HOOPS Commuicator如何从整体装配中创建破碎的装配零件和XML?

前言 虽然可以从某些本机CAD格式&#xff08;其子组件驻留在单独的文件中&#xff0c;例如CATIA V5、Creo - Pro/E、NX或SolidWorks&#xff09;创建破碎装配&#xff0c;但无法从整体装配文件&#xff08;例如IFC、Revit&#xff09;创建或3DXML。 本文介绍了一个示例&#…

12.C++常用的算法_遍历算法

文章目录 遍历算法1. for_each()代码工程运行结果 2. transform()代码工程运行结果 3. find()代码工程运行结果 遍历算法 1. for_each() 有两种方式&#xff1a; 1.普通函数 2.仿函数 代码工程 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<vect…

基于拉格朗日分布算法的电动汽车充放电调度MATLAB程序

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 程序简介 该模型主要做的是基于拉格朗日分布算法的电动汽车充放电调度模型。利用蒙特卡洛模拟法模拟出电动汽车负荷曲线&#xff0c;并求解出无序充电功率曲线和有序充电曲线&#xff0c;该模型在电动汽车个…

【Linux 学习】进程优先级和命令行参数!

1. 什么是优先级? 指定进程获取某种资源&#xff08;CPU&#xff09;的先后顺序&#xff1b; Linux 中优先级数字越小&#xff0c;优先级越高&#xff1b; 1.1 优先级和权限的区别&#xff1f; 权限 &#xff1a; 能不能做 优先级&#xff1a; 已经能了&#xff0c;但是获…

RX8111CE支持电池供电设备实现多计算芯片的数据交互

随着电池技术的发展&#xff0c;其容量和质量得到了显著提高。在目前的电池供电设备中&#xff0c;常常也会将传统主处理器和协处理器的结构应用到其中&#xff0c;这种计算机结构的引入大幅度提高了电池供电设备的计算能力&#xff0c;但是对设计也提出了更高的要求。在时钟系…

【Linux】虚拟化技术docker搭建SuitoCRM系统及汉化

CRM系统 CRM&#xff08;Customer Relationship Management&#xff0c;客户关系管理&#xff09;系统是一种用于管理和优化企业与客户关系的软件工具。在商业竞争激烈的现代社会中&#xff0c;CRM系统已成为许多企业提高销售、增强客户满意度和实现持续增长的重要工具。 搭建…

FMEA风险分析中几个常用的模型——SunFMEA软件

FMEA风险分析是确保风险管理成效的重要环节之一。为了很好地实施风险分析&#xff0c;风险管理专家开发了许多模型帮助其顺利实施&#xff0c;这些模型包括&#xff1a;领结模型、风险指数模型、因果模型、安全栅分析模型等。今天SunFMEA软件系统和大家一起分享这几种常用得模型…

libVLC 提取视频帧使用QGraphicsView渲染

在前面章节中&#xff0c;我们讲解了如何使用QWidget渲染每一帧视频数据&#xff0c;这种方法对 CPU 负荷较高。 libVLC 提取视频帧使用QWidget渲染-CSDN博客 后面又讲解了使用OpenGL渲染每一帧视频数据&#xff0c;使用 OpenGL去绘制&#xff0c;利用 GPU 减轻 CPU 计算负荷…

【前端捉鬼记】使用nvm切换node版本后再用node -v查看仍然是原来的版本

今天遇到一个诡异的问题&#xff0c;使用nvm切换node版本&#xff0c;明明提示已经切换成功&#xff0c;可是再次查看node版本还是之前的&#xff01; 尝试了很多办法&#xff0c;比如重新打开一个cmd窗口、切换前执行nvm install version都没成功&#xff0c;直到找到这篇文章…

mapv修改源码实现图标和管道到统一页面显示,图标和管道和点击

一、效果图 二、背景 map 地图添加marker&#xff0c;是操作的dom&#xff0c;而mapv是使用的canvas方式&#xff0c;所以性能要好 三、Mapv和MapVGL的区别 百度地图 JavaScript API GL快速升级 和mapVGL的使用 Mapv 是一款基于百度地图的大数据可视化开源库&#xff0c;可以…

安卓的认证测试

1 CTS CTS 是 Android 兼容性测试套件&#xff0c;用于验证设备是否符合 Android 平台的兼容性标准。它包含一系列测试用例&#xff0c;涵盖了设备的各个方面&#xff0c;如硬件功能、软件功能、API 的正确实现等。通过 CTS 测试&#xff0c;设备厂商可以确保其设备符合 Andro…

Fecify 商品标签功能

关于商品标签 商品标签是指商家可以在展示商品时&#xff0c;自己创建一个自定义标签&#xff0c;可自定义某个关键词或短语。这样顾客在浏览商城时&#xff0c;只需要通过标签就能看到更直观的展示信息。 商品标签可以按照用户的属性、行为、偏好等进行分类&#xff0c;标签要…

基于springboot实现服装厂服装生产管理系统项目【项目源码+论文说明】

基于springboot实现服装生产管理系统演示 摘要 本协力服装厂服装生产管理系统设计目标是实现协力服装厂服装生产的信息化管理&#xff0c;提高管理效率&#xff0c;使得协力服装厂服装生产管理作规范化、科学化、高效化。 本文重点阐述了协力服装厂服装生产管理系统的开发过程…

YOLOv9改进策略 :卷积魔改 | 变形条状卷积,魔改DCNv3二次创新

💡💡💡本文独家改进: 变形条状卷积,DCNv3改进版本,不降低精度的前提下相比较DCNv3大幅度运算速度 💡💡💡强烈推荐:先到先得,paper级创新,直接使用; 💡💡💡创新点:1)去掉DCNv3中的Mask;2)空间域上的双线性插值转改为轴上的线性插值; 💡💡💡…