Netty心跳机制-长连接
前文需求回顾
完成对红酒窖的室内温度采集及监控功能。由本地应用程序+温度传感器定时采集室内温度上报至服务器,如果温度 >20 °C 则由服务器下发重启空调指令,如果本地应用长时间不上传温度给服务器,则给户主手机发送一条预警短信。
Netty入门篇-从双向通信开始「上文」
上篇算是完成简单的双向通信了,我们接着看看 “如果本地应用长时间不上传温度给服务器…”,很明显客户端有可能挂了嘛,所以怎么实现客户端与服务端的长连接就是本文要实现的了。
什么是心跳机制
百度百科:心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制。
简单说,这个心跳机制是由客户端主动发起的消息,每隔一段时间就向服务端发送消息,告诉服务端自己还没死,可不要给户主发送预警短信啊。
如何实现心跳机制
1、客户端代码修改
我们需要改造一下上节中客户端的代码,首先是在责任链中增加一个心跳逻辑处理类HeartbeatHandler
public class NettyClient {private static String host = "127.0.0.1";public static void main(String[] args) {NioEventLoopGroup workerGroup = new NioEventLoopGroup();Bootstrap bootstrap = new Bootstrap();bootstrap// 1.指定线程模型.group(workerGroup)// 2.指定 IO 类型为 NIO.channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true).option(ChannelOption.TCP_NODELAY, true)// 3.IO 处理逻辑.handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) {ch.pipeline().addLast(new IdleStateHandler(0, 10, 0)).addLast(new StringDecoder()).addLast(new StringEncoder()).addLast(new HeartbeatHandler()).addLast(new NettyClientHandler());}});// 4.建立连接bootstrap.connect(host, 8070).addListener(future -> {if (future.isSuccess()) {System.out.println("连接成功!");} else {System.err.println("连接失败!");}});}
}
没什么变化,主要是增加了HeartbeatHandler
,我们来看看这个类:
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import java.nio.charset.Charset;
import java.time.LocalTime;public class HeartbeatHandler extends ChannelInboundHandlerAdapter {@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent) {IdleStateEvent idleStateEvent = (IdleStateEvent) evt;if (idleStateEvent.state() == IdleState.WRITER_IDLE) {System.out.println("10秒了,需要发送消息给服务端了" + LocalTime.now());//向服务端送心跳包ByteBuf buffer = getByteBuf(ctx);//发送心跳消息,并在发送失败时关闭该连接ctx.writeAndFlush(buffer).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);}} else {super.userEventTriggered(ctx, evt);}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {System.out.println("捕获的异常:" + cause.getMessage());ctx.channel().close();}private ByteBuf getByteBuf(ChannelHandlerContext ctx) {// 1. 获取二进制抽象 ByteBufByteBuf buffer = ctx.alloc().buffer();String time = "heartbeat:客户端心跳数据:" + LocalTime.now();// 2. 准备数据,指定字符串的字符集为 utf-8byte[] bytes = time.getBytes(Charset.forName("utf-8"));// 3. 填充数据到 ByteBufbuffer.writeBytes(bytes);return buffer;}}
还是继承自ChannelInboundHandlerAdapter
,不过这次重写的是userEventTriggered()
方法,这个方法在客户端的所有ChannelHandler
中,如果10s内没有发生write事件时触发,所以我们在该方法中给服务端发送心跳消息。
业务逻辑处理类NettyClientHandler
没有改动,代码如下:
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Random;public class NettyClientHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) {System.out.println(new Date() + ": 客户端写出数据");// 1. 获取数据ByteBuf buffer = getByteBuf(ctx);// 2. 写数据ctx.channel().writeAndFlush(buffer);}private ByteBuf getByteBuf(ChannelHandlerContext ctx) {// 1. 获取二进制抽象 ByteBufByteBuf buffer = ctx.alloc().buffer();Random random = new Random();double value = random.nextDouble() * 14 + 8;String temp = "获取室内温度:" + value;// 2. 准备数据,指定字符串的字符集为 utf-8byte[] bytes = temp.getBytes(Charset.forName("utf-8"));// 3. 填充数据到 ByteBufbuffer.writeBytes(bytes);return buffer;}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {System.out.println(new Date() + ": 客户端读到数据 -> " + msg.toString());}}
对如上代码不了解的可以回看上一节:Netty入门篇-从双向通信开始
2、服务端代码修改
服务端代码主要是开启TCP底层心跳机制支持,.childOption(ChannelOption.SO_KEEPALIVE, true)
,其他的代码并没有改动:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;public class NettyServer {public static void main(String[] args) {NioEventLoopGroup bossGroup = new NioEventLoopGroup();NioEventLoopGroup workerGroup = new NioEventLoopGroup();ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(bossGroup, workerGroup)// 指定Channel.channel(NioServerSocketChannel.class)//服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数.option(ChannelOption.SO_BACKLOG, 1024)//设置TCP长连接,一般如果两个小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文.childOption(ChannelOption.SO_KEEPALIVE, true)//将小的数据包包装成更大的帧进行传送,提高网络的负载.childOption(ChannelOption.TCP_NODELAY, true).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) {ch.pipeline().addLast(new NettyServerHandler());}});serverBootstrap.bind(8070);}}
我们再来看看服务端的业务处理类 NettyServerHandler
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.nio.charset.Charset;
import java.util.Date;public class NettyServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf byteBuf = (ByteBuf) msg;String message = byteBuf.toString(Charset.forName("utf-8"));System.out.println(new Date() + ": 服务端读到数据 -> " + message);/** 心跳数据是不发送数据 **/if(!message.contains("heartbeat")){ByteBuf out = getByteBuf(ctx);ctx.channel().writeAndFlush(out);}}private ByteBuf getByteBuf(ChannelHandlerContext ctx) {byte[] bytes = "我是发送给客户端的数据:请重启冰箱!".getBytes(Charset.forName("utf-8"));ByteBuf buffer = ctx.alloc().buffer();buffer.writeBytes(bytes);return buffer;}}
对channelRead()
方法增加了一个 if 判断,判断如果包含heartbeat
字符串就认为这是客户端发过来的心跳,这种判断是非常low的,因为到目前为止我们一直是用简单字符串来传递数据的,上边传递的数据就直接操作字符串;那么问题来了,如果我们想传递对象怎么搞呢?下节写。我们先来看一下如上代码客户端与服务端运行截图:
服务端
客户端
至此,整个心跳机制就完成了,这样每隔10秒客户端就会给服务端发送一个心跳消息,下节我们通过了解通协议以完善心跳机制的代码。
18年专科毕业后,我创建了一个java相关的公众号,用来记录自己的学习之路,感兴趣的小伙伴可以关注一下:小伟后端笔记
Netty心跳机制-长连接相关推荐
- springboot心跳检测_springboot结合netty+心跳机制
springboot结合netty+心跳机制 netty简介(by 百度百科)Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目.Netty提供异步的.事件驱动的网络应 ...
- ZooKeeper客户端源码(一)——向服务端建立连接+会话建立+心跳保持长连接
首发CSDN:徐同学呀,原创不易,转载请注明源链接.我是徐同学,用心输出高质量文章,希望对你有所帮助. 一.从ZooKeeper实例初始化开始 ZooKeeper 提供了原生的客户端库,虽然不好用,但 ...
- mqtt连接失败_Netty实战:如何让单机下Netty支持百万长连接?
单机下能不能让我们的网络应用支持百万连接?可以,但是有很多的工作要做.而且要考虑到单机的系统资源消耗能否支撑百万并发 一.操作系统优化 首先就是要突破操作系统的限制. 在Linux平台上,无论编写客户 ...
- bootstrap外不引用连接_网络编程Netty IoT百万长连接优化,万字长文精讲
IoT是什么 The Internet of things的简称IoT,即是物联网的意思 IoT推送系统的设计 比如说,像一些智能设备,需要通过APP或者微信中的小程序等,给设备发送一条指令,让这个设 ...
- Netty 心跳机制及断线重连
1.心跳检测 心跳检测是在TCP长连接中,客户端和服务端定时向对方发送数据包通知对方自己还在线,保证连接的有效性的一种机制. 为什么使用心跳检测? 假死:如果底层的TCP连接(socket连接)已经断 ...
- Netty——心跳机制与断线重连
心跳机制与断线重连 心跳机制 IdleStateHandler 客户端 服务端 测试 正常情况 异常情况 总结 断线重连 为了保证系统的稳定性,心跳机制和断线重连可是必不可少的,而这两个在Netty中 ...
- Mqtt ----心跳机制 长链接 ping
Mqtt ----心跳机制 心跳机制 Keep Alive指定连接最大空闲时间T,当客户端检测到连接空闲时间超过T时,必须向Broker发送心跳报文PINGREQ,Broker收到心跳请求后返回心跳响 ...
- cnpm 网络不能连接_Android 架构之长连接技术
读者好,很久没见,近期恢复更新啦,内容以Android技术.大前端.程序猿技术成长.跳槽内推等为主,欢迎继续关注. 上一篇文章<Android 架构之网络框架(上)>中,我们谈过了网络框架 ...
- TCP长连接,心跳机制介绍
TCP长连接,心跳机制介绍 长连接 为何要长连接 心跳 心跳为何设置在服务器端 心跳维持长连接 TCP keep-alive的三个参数 参数的具体意义 心跳的使用场景 长连接 TCP经过三次握手建立连 ...
最新文章
- linux编写多进程程序实验,实验7 编写多进程程序
- 解决Jenkins升级时浏览器一直提示Please wait while Jenkins is restarting问题
- 系统快捷键被谁占用? 查看工具
- ps4连接r星服务器稳定,移植到不同平台的《GTA5》有什么变化?与八年前相比,差距这么大...
- leetcode 202. 快乐数 思考分析(哈希集合与双指针解)
- 各种服务器系统桌面,服务器系统云桌面
- 【算法】赫夫曼编码 解码 实际应用 文件的编码 解码
- Handler sendMessage 与 obtainMessage (sendToTarget)比较
- 201421410019 杨光裕 实验一
- Linux栈溢出漏洞原理,盘它!PWN栈溢出漏洞。
- python的flask实现接口_python+flask:实现POST接口功能
- 随书光盘资源下载/提取码(二)
- SSH框架的工作原理
- 极路由3C【C103B】刷breed再刷老毛子教程
- 相似度系列-5:语义方法:BERTSCORE: EVALUATING TEXT GENERATION WITH BERT
- IC验证面试必考-跨时钟域
- Zer0pts CTF 2020的web赛后记录+复现环境
- 使用C++模拟动态密码验证
- unity 实现物体破碎效果的一些方法 - 细雨淅淅
- No input file specified 出现的原因及解决方法
热门文章
- [vue] 分析下vue项目本地开发完成后部署到服务器后报404是什么原因呢?
- 工作310:uni-初始获取数据onload
- [js] json和对象有什么区别?
- 前端学习(2625):vs安装
- 前端学习(2216):react元素渲染
- 前端学习(1953)vue之电商管理系统电商系统之根据父类数据处理表单中的数据
- 前端学习(238):IE低版本常见bug
- 第四十一期:从Windows到鸿蒙——操作系统的前世与今生
- JS之返回数组指定元素的slice
- python编程基础题答案_大学moocPython编程基础题目及答案