前文需求回顾

完成对红酒窖的室内温度采集及监控功能。由本地应用程序+温度传感器定时采集室内温度上报至服务器,如果温度 >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心跳机制-长连接相关推荐

  1. springboot心跳检测_springboot结合netty+心跳机制

    springboot结合netty+心跳机制 netty简介(by 百度百科)Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目.Netty提供异步的.事件驱动的网络应 ...

  2. ZooKeeper客户端源码(一)——向服务端建立连接+会话建立+心跳保持长连接

    首发CSDN:徐同学呀,原创不易,转载请注明源链接.我是徐同学,用心输出高质量文章,希望对你有所帮助. 一.从ZooKeeper实例初始化开始 ZooKeeper 提供了原生的客户端库,虽然不好用,但 ...

  3. mqtt连接失败_Netty实战:如何让单机下Netty支持百万长连接?

    单机下能不能让我们的网络应用支持百万连接?可以,但是有很多的工作要做.而且要考虑到单机的系统资源消耗能否支撑百万并发 一.操作系统优化 首先就是要突破操作系统的限制. 在Linux平台上,无论编写客户 ...

  4. bootstrap外不引用连接_网络编程Netty IoT百万长连接优化,万字长文精讲

    IoT是什么 The Internet of things的简称IoT,即是物联网的意思 IoT推送系统的设计 比如说,像一些智能设备,需要通过APP或者微信中的小程序等,给设备发送一条指令,让这个设 ...

  5. Netty 心跳机制及断线重连

    1.心跳检测 心跳检测是在TCP长连接中,客户端和服务端定时向对方发送数据包通知对方自己还在线,保证连接的有效性的一种机制. 为什么使用心跳检测? 假死:如果底层的TCP连接(socket连接)已经断 ...

  6. Netty——心跳机制与断线重连

    心跳机制与断线重连 心跳机制 IdleStateHandler 客户端 服务端 测试 正常情况 异常情况 总结 断线重连 为了保证系统的稳定性,心跳机制和断线重连可是必不可少的,而这两个在Netty中 ...

  7. Mqtt ----心跳机制 长链接 ping

    Mqtt ----心跳机制 心跳机制 Keep Alive指定连接最大空闲时间T,当客户端检测到连接空闲时间超过T时,必须向Broker发送心跳报文PINGREQ,Broker收到心跳请求后返回心跳响 ...

  8. cnpm 网络不能连接_Android 架构之长连接技术

    读者好,很久没见,近期恢复更新啦,内容以Android技术.大前端.程序猿技术成长.跳槽内推等为主,欢迎继续关注. 上一篇文章<Android 架构之网络框架(上)>中,我们谈过了网络框架 ...

  9. TCP长连接,心跳机制介绍

    TCP长连接,心跳机制介绍 长连接 为何要长连接 心跳 心跳为何设置在服务器端 心跳维持长连接 TCP keep-alive的三个参数 参数的具体意义 心跳的使用场景 长连接 TCP经过三次握手建立连 ...

最新文章

  1. linux编写多进程程序实验,实验7 编写多进程程序
  2. 解决Jenkins升级时浏览器一直提示Please wait while Jenkins is restarting问题
  3. 系统快捷键被谁占用? 查看工具
  4. ps4连接r星服务器稳定,移植到不同平台的《GTA5》有什么变化?与八年前相比,差距这么大...
  5. leetcode 202. 快乐数 思考分析(哈希集合与双指针解)
  6. 各种服务器系统桌面,服务器系统云桌面
  7. 【算法】赫夫曼编码 解码 实际应用 文件的编码 解码
  8. Handler sendMessage 与 obtainMessage (sendToTarget)比较
  9. 201421410019 杨光裕 实验一
  10. Linux栈溢出漏洞原理,盘它!PWN栈溢出漏洞。
  11. python的flask实现接口_python+flask:实现POST接口功能
  12. 随书光盘资源下载/提取码(二)
  13. SSH框架的工作原理
  14. 极路由3C【C103B】刷breed再刷老毛子教程
  15. 相似度系列-5:语义方法:BERTSCORE: EVALUATING TEXT GENERATION WITH BERT
  16. IC验证面试必考-跨时钟域
  17. Zer0pts CTF 2020的web赛后记录+复现环境
  18. 使用C++模拟动态密码验证
  19. unity 实现物体破碎效果的一些方法 - 细雨淅淅
  20. No input file specified 出现的原因及解决方法

热门文章

  1. [vue] 分析下vue项目本地开发完成后部署到服务器后报404是什么原因呢?
  2. 工作310:uni-初始获取数据onload
  3. [js] json和对象有什么区别?
  4. 前端学习(2625):vs安装
  5. 前端学习(2216):react元素渲染
  6. 前端学习(1953)vue之电商管理系统电商系统之根据父类数据处理表单中的数据
  7. 前端学习(238):IE低版本常见bug
  8. 第四十一期:从Windows到鸿蒙——操作系统的前世与今生
  9. JS之返回数组指定元素的slice
  10. python编程基础题答案_大学moocPython编程基础题目及答案