Netty学习——实战篇5 Netty 心跳监测/WebSocket长连接编程 备份

 1 心跳监测

MyServer.java
public class MyServer {public static void main(String[] args) {NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);NioEventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.DEBUG)).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();//加入Netty提供的 IdleStateHandler/*说明:IdleStateHandler 是netty提供的处理空闲状态的处理器long readerIdleTime:表示多长时间没有读,就会发送一个心跳监测包 检测是否连接long writerIdelTime:表示多长时间没有写,就会发送一个心跳监测包 监测是否连接long allIdelTime:表示多长时间没有读写,就会发送一个心跳检测包 监测是否连接*/pipeline.addLast(new IdleStateHandler(3,5,7, TimeUnit.SECONDS));//加入自定义Handler,对空闲检测进一步处理pipeline.addLast(new MyServerHandler());}});ChannelFuture channelFuture = serverBootstrap.bind(8000).sync();channelFuture.channel().closeFuture().sync();}catch (Exception e){e.printStackTrace();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
MyServerHandler.java 
@Slf4j
public class MyServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if(evt instanceof IdleStateEvent){IdleStateEvent event = (IdleStateEvent) evt;String eventType = null;switch (event.state()){case READER_IDLE:eventType = "读空闲";break;case WRITER_IDLE:eventType = "写空闲";break;case ALL_IDLE:eventType = "读写空闲";break;}log.info("{},---超时时间---,{}",ctx.channel().remoteAddress(),eventType);log.info("服务器做相应处理");//如果发生空闲,关闭通道ctx.channel().close();}}
}
NettyChatClient.java 
@Slf4j
public class NettyChatClient {private  String host;private  int port;public NettyChatClient(String host, int port) {this.host = host;this.port = port;}private void run(){NioEventLoopGroup loopGroup = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(loopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast("decoder",new StringDecoder());pipeline.addLast("encoder",new StringEncoder());pipeline.addLast(new NettyChatClientHandler());}});ChannelFuture channelFuture = bootstrap.connect(host, port).sync();Channel channel = channelFuture.channel();log.info("客户端连接成功,地址是:{}",channel.remoteAddress());Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()){String msg = scanner.nextLine();channel.writeAndFlush(msg + "\r\n");}}catch (Exception e){e.printStackTrace();}finally {loopGroup.shutdownGracefully();}}public static void main(String[] args) {new NettyChatClient("127.0.0.1",8000).run();}
}
NettyChatClientHandler.java
public class NettyChatClientHandler extends SimpleChannelInboundHandler<String> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {System.out.println(msg.trim());}
}

服务端运行结果:

2  WebSocket实现服务器和客户端长连接

2.1 需求

        (1)Http协议是无状态的,浏览器和服务器之间的请求响应一次,下一次会重新创建连接。

        (2)实现基于webSocket的长连接的全双工的交互。

        (3)改变Http协议多次请求的约束,实现长连接,服务端可以发送消息给浏览器。

        (4)客户端浏览器和服务端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务端也会感知。

服务端代码:MyServer.java

public class MyServer {public static void main(String[] args) {NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);NioEventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();//基于http协议,使用和图片的编解码pipeline.addLast(new HttpServerCodec());//以块方式写,添加chunkedwritehandler处理器pipeline.addLast(new ChunkedWriteHandler());/*说明1. http数据在传输过程中是分段, HttpObjectAggregator ,就是可以将多个段聚合2. 这就就是为什么,当浏览器发送大量数据时,就会发出多次http请求*/pipeline.addLast(new HttpObjectAggregator(8192));/*说明1. 对应websocket ,它的数据是以 帧(frame) 形式传递2. 可以看到WebSocketFrame 下面有六个子类3. 浏览器请求时 ws://localhost:7000/hello 表示请求的uri4. WebSocketServerProtocolHandler 核心功能是将 http协议升级为 ws协议 , 保持长连接5. 是通过一个 状态码 101*/pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));//自定义handlerpipeline.addLast(new MyTextWebSocketFrameHandler());}});ChannelFuture channelFuture = serverBootstrap.bind(8000).sync();channelFuture.channel().closeFuture().sync();}catch (Exception e){e.printStackTrace();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}

自定义Handler:MyTextWebSocketFrameHandler.java

@Slf4j
public class MyTextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {log.info("服务器接收消息:{}",msg.text());//回复消息ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间"+ LocalDateTime.now()+ " " +msg.text()));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {log.info("发生异常:{}",cause.getMessage());ctx.close();}@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {log.info("handlerAdded 被调用,channel id 是:{}",ctx.channel().id().asLongText());log.info("handlerAdded 被调用,channel id 是:{}",ctx.channel().id().asShortText());}@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {log.info("handlerRemoved 被调用,channel id 是:{}",ctx.channel().id().asLongText());}
}

客户端代码:hello.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<script>var socket;//判断当前浏览器是否支持websocketif(window.WebSocket) {//go onsocket = new WebSocket("ws://localhost:8000/hello");//相当于channelReado, ev 收到服务器端回送的消息socket.onmessage = function (ev) {var rt = document.getElementById("responseText");rt.value = rt.value + "\n" + ev.data;}//相当于连接开启(感知到连接开启)socket.onopen = function (ev) {var rt = document.getElementById("responseText");rt.value = "连接开启了.."}//相当于连接关闭(感知到连接关闭)socket.onclose = function (ev) {var rt = document.getElementById("responseText");rt.value = rt.value + "\n" + "连接关闭了.."}} else {alert("当前浏览器不支持websocket")}//发送消息到服务器function send(message) {if(!window.socket) { //先判断socket是否创建好return;}if(socket.readyState == WebSocket.OPEN) {//通过socket 发送消息socket.send(message)} else {alert("连接没有开启");}}
</script><form onsubmit="return false"><textarea name="message" style="height: 300px; width: 300px"></textarea><input type="button" value="发生消息" onclick="send(this.form.message.value)"><textarea id="responseText" style="height: 300px; width: 300px"></textarea><input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''"></form>
</body>
</html>

服务端运行结果:

客户端运行结果:

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

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

相关文章

leetcode145--二叉树的后序遍历

1. 题意 求后序遍历 2. 题解 2.1 递归 class Solution { public:void addPost(TreeNode *root, vector<int> &res) {if ( nullptr root)return ;addPost(root->left, res);addPost(root->right, res);res.emplace_back( root->val );}vector<int>…

设计前后端系统以处理长时间运行的计算任务并提供缓存支持

后端设计 1. 任务队列 创建一个任务队列来存储提交的计算任务。 Component public class TaskQueue {private final Queue<CalculationTask> queue new LinkedList<>();public synchronized void addTask(CalculationTask task) {queue.add(task);}public sync…

C++ 全量枚举

在C中&#xff0c;全量枚举通常指的是为一个类型的所有可能值创建一个枚举。这样可以使代码更具可读性&#xff0c;特别是当你需要处理有限的、固定的值集合时。 下面是一个简单的例子&#xff0c;展示了如何定义和使用枚举&#xff1a; #include <iostream>// 定义枚举…

lvresize与lvextend扩容逻辑卷的区别

这两条命令都是用来扩展逻辑卷&#xff08;Logical Volume&#xff09;的命令&#xff0c;但是有一些区别&#xff1a; 1. lvresize命令&#xff1a; - lvresize命令是用来调整逻辑卷的大小的&#xff0c;可以缩小或扩大逻辑卷的大小。 - 在使用lvresize命令时&#xff0c;需…

20240425 每日一题:2739. 总行驶距离

题目简介 卡车有两个油箱。给你两个整数&#xff0c;mainTank 表示主油箱中的燃料&#xff08;以升为单位&#xff09;&#xff0c;additionalTank 表示副油箱中的燃料&#xff08;以升为单位&#xff09;。 该卡车每耗费 1 升燃料都可以行驶 10 km。每当主油箱使用了 5 升燃…

学习Docker笔记

在23号刚刚学完新版本的docker还想说回去继续学习老版本的springcloud课程里面的docker 结果一看黑马首页新版本课程出了&#xff0c;绷不住了。以下是我学习新版本docker的笔记&#xff0c;记录了我学习过程遇到的各种bug和解决&#xff0c;也参考了黑马老师的笔记&#xff1a…

TDengine高可用探讨

提到数据库&#xff0c;不可避免的要考虑高可用HA&#xff08;High Availability&#xff09;。但是很多人对高可用的理解并不是很透彻。 要搞清高可用需要回答以下几个问题&#xff1a; 什么是高可用&#xff1f;为什么需要高可用&#xff1f;高可用需要达到什么样的目标&am…

Unity射线实现碰撞检测(不需要rigbody组件)

使用physic.CapsulCast&#xff08;&#xff09;&#xff1b; 前面3个参数生成一个胶囊体&#xff0c; 向着发射方向&#xff0c;发射出一串的胶囊&#xff08;没有最大距离&#xff09; 有最大距离&#xff0c;可以节约性能开销。 physic.CapsulCast&#xff08;&#xff0…

记录一次使用springboot 3 用gradle脚本的踩坑记录

问题1 . 下载插件或者gradle以及gradle plus有问题 报错&#xff1a;Could not resolve org.springframework.boot:spring-boot-gradle-plugin:3.3.0-RC 解决方法&#xff1a;使用如下配置 settings.gradle的 pluginManagement下 repositories是插件的依赖或者插件的下载地址…

easypoi 导出增加自增序列

要求&#xff1a;使用easypoi导出Excel的时候&#xff0c;要求增加”序号“列&#xff0c;从1开始增加。这列与业务数据无关&#xff0c;只是展示用&#xff0c;便于定位。如下图所示 实现方式&#xff1a;Java对象新增一列&#xff0c;注意name "序号", format &…

测试工程师常见面试题及答案

什么是软件测试生命周期&#xff08;STLC&#xff09;? STLC是一个特定的阶段序列&#xff0c;用于测试软件或应用程序。它包括以下阶段&#xff1a; 需求分析报告计划测试设计测试环境设置测试执行测试闭环或评估 解释黑盒测试和白盒测试的区别 黑盒测试是一种测试方法&#…

Linux-缓冲区(简单理解)

1. 缓冲区是什么 缓冲区就是一段内存空间。 2. 为什么要有缓冲区 IO写入有两种&#xff1a; 写透模式&#xff08;WT&#xff09; 成本高&#xff0c;效率低写回模式&#xff08;WB&#xff09; 成本低&#xff0c;效率高 写透模式&#xff1a;每次的文件写入都要立即刷新…

【006期】用Processing写一个根据音乐变化的黑白格游戏

黑白格游戏 简述准备工作游戏设计实现代码注意事项优化二次优化 简述 为了创建一个简单的根据音乐变化的黑白格游戏&#xff0c;我们将利用Processing的简洁性和直观操作来实现。游戏的核心思想是&#xff1a;屏幕上呈现多个黑白格&#xff0c;随着音乐的播放&#xff0c;格子…

使用ClickHouse和Terraform进行CI/CD

本文字数&#xff1a;11047&#xff1b;估计阅读时间&#xff1a;28 分钟 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 本文在公众号【ClickHouseInc】首发 简介 在 ClickHouse&#xff0c;我们致力于以 API 为先的开发方式来构建 ClickHouse Cloud。用户通过用户界面…

arm64-v8a和armeabi-v7a分别是什么?它们之间有什么区别

2024年4月22日&#xff0c;周一上午 arm64-v8a 和 armeabi-v7a 都是针对不同的 ARM 处理器架构的编译版本&#xff0c;它们之间的主要区别在于处理器架构和指令集。 arm64-v8a&#xff1a; arm64-v8a 是针对 ARMv8 架构的 64 位处理器的编译版本。ARMv8 架构引入了 64 位指令集…

如何快速学习盲打键盘的指法

学习盲打键盘的指法需要一定的时间和练习&#xff0c;但是以下几个方法可以帮助你加快学习的速度&#xff1a; 掌握正确的手位&#xff1a;了解标准的键盘布局以及手指应该放置的位置是学习盲打的第一步。在QWERTY键盘上&#xff0c;你的左手应该放在ASDF键上&#xff0c;右手应…

人工智能入门(一):基于Pytorch的手写数字识别模型

前言&#xff1a; 因为还在上学&#xff0c;时间不太够用&#xff0c;很多内容写到后面心有余力不足&#xff0c;未来有时间我会慢慢补充。人工智能的知识涉猎范围广又杂乱无章&#xff0c;啃书或上课学到的知识往往很早就过时了或者离实际的项目无关。所以&#xff0c;我很希…

VS调试、debug和release、栈区底层简单介绍、const 修饰指针变量介绍

文章目录 前言一、调试二、debug和release三、调试需要多用&#xff0c;多熟悉四、栈区底层简单介绍五、优秀的代码&#xff1a;常见的coding技巧: 六、const 修饰指针变量1. const 出现在 * 左边2. const 出现在 * 右边 七、strcpy函数的仿写1.版本12. 版本23. 版本34. 版本4 …

Corner case 数据处理方法

背景 特斯拉的纯视觉处理方法&#xff0c;在处理corner case的过程中&#xff0c;面对着数据量少&#xff0c;而且难以采集的问题。为了提升模型的数据量&#xff0c;通过一定的数据处理方法进行corner case的数据生成&#xff0c;不失为一种有效的办法。 "Corner case&…

Andorid进程间通信之 UNIX SOCKET

1&#xff0c;什么是UNIX SOCKET UNIX SOCKET&#xff0c;域套接字&#xff0c;UNIX SOCKET可用于同一台设备进程间通信&#xff0c;它不需要经过网络协议栈&#xff0c;不需要打包拆包、计算校验和、维护序列号应答等&#xff0c;只需要将数据从一个进程复制到另一个进程&…