JAVA实现心跳检测【长连接】
文章目录
- 1、心跳机制简介
- 2、心跳机制实现方式
- 3、客户端
- 4 、服务端
- 5、代码实现
- 5.1 KeepAlive.java
- 5.2 MyClient.java
- 5.3 MyServer
- 5.4 测试结果
1、心跳机制简介
在分布式系统中,分布在不同主机上的节点需要检测其他节点的状态,如服务器节点需要检测从节点是否失效。为了检测对方节点的有效性,每隔固定时间就发送一个固定信息给对方,对方回复一个固定信息,如果长时间没有收到对方的回复,则断开与对方的连接。
发包方既可以是服务端,也可以是客户端,这要看具体实现。因为是每隔固定时间发送一次,类似心跳,所以发送的固定信息称为心跳包。心跳包一般为比较小的包,可根据具体实现。心跳包主要应用于长连接的保持与短线链接。
一般而言,应该客户端主动向服务器发送心跳包,因为服务器向客户端发送心跳包会影响服务器的性能。
实现原理:长连接的维持,是要客户端程序,定时向服务端程序,发送一个维持连接包的。如果,长时间未发送维持连接包,服务端程序将断开连接。
2、心跳机制实现方式
心跳机制有两种实现方式,一种基于TCP自带的心跳包,TCP的SO_KEEPALIVE选项可以,系统默认的默认跳帧频率为2小时,超过2小时后,本地的TCP 实现会发送一个数据包给远程的 Socket. 如果远程Socket 没有发回响应, TCP实现就会持续尝试 11 分钟, 直到接收到响应为止。 否则就会自动断开Socket连接。但TCP自带的心跳包无法检测比较敏感地知道对方的状态,默认2小时的空闲时间,对于大多数的应用而言太长了。可以手工开启KeepAlive功能并设置合理的KeepAlive参数。
另一种在应用层自己进行实现,基本步骤如下:
- 1、Client使用定时器,不断发送心跳;
- 2、Server收到心跳后,回复一个包;
- 3、Server为每个Client启动超时定时器,如果在指定时间内没有收到Client的心跳包,则Client失效。
3、客户端
Client通过持有Socket的对象,可以随时(使用sendObject方法)发送Massage Object(消息)给服务端。
如果keepAliveDelay毫秒(程序中是2秒)内未发送任何数据,则自动发送一个KeepAlive Object(心跳)给服务端,用于维持连接。
由于,我们向服务端,可以发送很多不同的消息对象,服务端也可以返回不同的对象。所以,对于返回对象的处理,要编写具体的ObjectAction实现类进行处理。通过Client.addActionMap方法进行添加。这样,程序会回调处理
4 、服务端
由于客户端会定时(keepAliveDelay毫秒)发送维持连接的信息过来,所以,服务端要有一个检测机制。
即当服务端receiveTimeDelay毫秒(程序中是3秒)内未接收任何数据,则自动断开与客户端的连接。
ActionMapping的原理与客户端相似(相同)。
通过添加相应的ObjectAction实现类,可以实现不同对象的响应、应答过程。
5、代码实现
5.1 KeepAlive.java
package com.zyz.mynative.demo02;/*** @author zyz* @version 1.0* @data 2023/2/15 11:05* @Description:*/
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;/**** 维持连接的消息对象(心跳对象)* @author zyz* @version 1.0* @data 2023/2/15 11:05* @Description:*/
public class KeepAlive implements Serializable{private static final long serialVersionUID = -2813120366138988480L;/* 覆盖该方法,仅用于测试使用。* @see java.lang.Object#toString()*/@Overridepublic String toString() {return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"\t维持连接包";}}
5.2 MyClient.java
package com.zyz.mynative.demo02;/*** @author zyz* @version 1.0* @data 2023/2/15 11:06* @Description:*/import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ConcurrentHashMap;/*** C/S架构的客户端对象,持有该对象,可以随时向服务端发送消息。** @author zyz* @version 1.0* @data 2023/2/15 11:06* @Description:*/
public class MyClient {/*** 处理服务端发回的对象,可实现该接口。*/public static interface ObjectAction {void doAction(Object obj, MyClient client);}public static final class DefaultObjectAction implements ObjectAction {@Overridepublic void doAction(Object obj, MyClient client) {System.out.println("处理:\t" + obj.toString());}}private String serverIp;private int port;private Socket socket;//连接状态private boolean running = false;/*** 最后一次发送数据的时间*/private long lastSendTime;/*** 用于保存接收消息对象类型及该类型消息处理的对象*/private ConcurrentHashMap<Class, ObjectAction> actionMapping = new ConcurrentHashMap<Class, ObjectAction>();public MyClient(String serverIp, int port) {this.serverIp = serverIp;this.port = port;}public void start() throws UnknownHostException, IOException {if (running) {return;}socket = new Socket(serverIp, port);System.out.println("本地端口:" + socket.getLocalPort());lastSendTime = System.currentTimeMillis();running = true;//保持长连接的线程,每隔2秒项服务器发一个一个保持连接的心跳消息new Thread(new KeepAliveWatchDog()).start();//接受消息的线程,处理消息new Thread(new ReceiveWatchDog()).start();}public void stop() {if (running) {running = false;};}/*** 添加接收对象的处理对象。** @param cls 待处理的对象,其所属的类。* @param action 处理过程对象。*/public void addActionMap(Class<Object> cls, ObjectAction action) {actionMapping.put(cls, action);}public void sendObject(Object obj) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());oos.writeObject(obj);System.out.println("发送:\t" + obj);oos.flush();}class KeepAliveWatchDog implements Runnable {long checkDelay = 10;//两秒钟检测一次long keepAliveDelay = 2000;@Overridepublic void run() {while (running) {if (System.currentTimeMillis() - lastSendTime > keepAliveDelay) {try {MyClient.this.sendObject(new KeepAlive());} catch (IOException e) {e.printStackTrace();MyClient.this.stop();}lastSendTime = System.currentTimeMillis();} else {try {Thread.sleep(checkDelay);} catch (InterruptedException e) {e.printStackTrace();MyClient.this.stop();}}}}}class ReceiveWatchDog implements Runnable {@Overridepublic void run() {while (running) {try {InputStream in = socket.getInputStream();if (in.available() > 0) {ObjectInputStream ois = new ObjectInputStream(in);Object obj = ois.readObject();System.out.println("接收:\t" + obj);ObjectAction oa = actionMapping.get(obj.getClass());oa = oa == null ? new DefaultObjectAction() : oa;oa.doAction(obj, MyClient.this);} else {Thread.sleep(10);}} catch (Exception e) {e.printStackTrace();MyClient.this.stop();}}}}public static void main(String[] args) throws UnknownHostException, IOException {String serverIp = "127.0.0.1";int port = 65432;MyClient client = new MyClient(serverIp, port);client.start();}
}
5.3 MyServer
package com.zyz.mynative.demo02;import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;/*** C/S架构的服务端对象。** @author zyz* @version 1.0* @data 2023/2/15 11:08* @Description:*/
public class MyServer {/*** 要处理客户端发来的对象,并返回一个对象,可实现该接口。*/public interface ObjectAction {Object doAction(Object rev, MyServer server);}public static final class DefaultObjectAction implements ObjectAction {@Overridepublic Object doAction(Object rev, MyServer server) {System.out.println("处理并返回:" + rev);return rev;}}private int port;private volatile boolean running = false;private long receiveTimeDelay = 3000;/*** ConcurrentHashMap是线程安全的,ConcurrentHashMap并非锁住整个方法,* 而是通过原子操作和局部加锁的方法保证了多线程的线程安全,且尽可能减少了性能损耗。*/private ConcurrentHashMap<Class, ObjectAction> actionMapping = new ConcurrentHashMap<Class, ObjectAction>();private Thread connWatchDog;public MyServer(int port) {this.port = port;}/*** 通过继承Runnable ,开启线程*/public void start() {if (running) {return;};running = true;connWatchDog = new Thread(new ConnWatchDog());connWatchDog.start();}@SuppressWarnings("deprecation")public void stop() {if (running) {running = false;};if (connWatchDog != null) {connWatchDog.stop();};}public void addActionMap(Class<Object> cls, ObjectAction action) {actionMapping.put(cls, action);}/*** 继承Runnable 重写run方法、实现线程*/class ConnWatchDog implements Runnable {@Overridepublic void run() {try {ServerSocket ss = new ServerSocket(port, 5);while (running) {Socket s = ss.accept();new Thread(new SocketAction(s)).start();}} catch (IOException e) {e.printStackTrace();MyServer.this.stop();}}}/*** 继承Runnable 重写run方法、实现线程,通过匿名内部类*/class SocketAction implements Runnable {Socket s;boolean run = true;long lastReceiveTime = System.currentTimeMillis();public SocketAction(Socket s) {this.s = s;}@Overridepublic void run() {while (running && run) {if (System.currentTimeMillis() - lastReceiveTime > receiveTimeDelay) {overThis();} else {try {InputStream in = s.getInputStream();if (in.available() > 0) {ObjectInputStream ois = new ObjectInputStream(in);Object obj = ois.readObject();lastReceiveTime = System.currentTimeMillis();System.out.println("接收:\t" + obj);ObjectAction oa = actionMapping.get(obj.getClass());oa = oa == null ? new DefaultObjectAction() : oa;Object out = oa.doAction(obj, MyServer.this);if (out != null) {ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());oos.writeObject(out);oos.flush();}} else {Thread.sleep(10);}} catch (Exception e) {e.printStackTrace();overThis();}}}}private void overThis() {if (run) {run = false;};if (s != null) {try {s.close();} catch (IOException e) {e.printStackTrace();}}System.out.println("关闭:" + s.getRemoteSocketAddress());}}public static void main(String[] args) {int port = 65432;MyServer server = new MyServer(port);server.start();}}
5.4 测试结果
先开启服务端,在开启客户端

参考资料:
- 1、java实现心跳机制
- 2、java实现长连接
相关文章:
JAVA实现心跳检测【长连接】
文章目录1、心跳机制简介2、心跳机制实现方式3、客户端4 、服务端5、代码实现5.1 KeepAlive.java5.2 MyClient.java5.3 MyServer5.4 测试结果1、心跳机制简介 在分布式系统中,分布在不同主机上的节点需要检测其他节点的状态,如服务器节点需要检测从节点…...
python3.9安装和pandas安装踩坑处理
0、先决条件:系统内最好先安装有gcc、libffi-devel等 1、安装包下载 https://www.python.org/downloads/source/ 2、解压安装包并上传到/usr/local/python3.9 3、打开shell cd /usr/local/python3.9要先把python3.9的所有文件复制到/usr/local/python3.9才会成功…...
2023.2.15每日一题——867. 转置矩阵
每日一题题目描述解题核心解法一:二维表示 模拟解法二:一维表示 模拟题目描述 题目链接:867. 转置矩阵 给你一个二维整数数组 matrix, 返回 matrix 的 转置矩阵 。 矩阵的 转置 是指将矩阵的主对角线翻转,交换矩阵…...
【人脸识别】Partial-FC:让你在一台机器上训练1000万个id人脸数据集成为可能!
论文题目:”Killing Two Birds with One Stone: Efficient and Robust Training of Face Recognition CNNs by Partial FC“ -CVPR 2022 代码地址:https://arxiv.org/pdf/2203.15565.pdf 代码地址:https://github.com/deepinsight/insightfac…...
递归方法读取任意深度的 JSON 对象的键值
有以下json字符串 {"name":"John","age":30,"address":{"city":"New York","state":"NY","zip":"10001","coordinates":{"latitude":40.712776,&q…...
黑马redis学习记录:分布式锁
一、基本原理和实现方式对比 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行…...
对React-Fiber的理解,它解决了什么问题?
对React-Fiber的理解,它解决了什么问题?Fiber用来解决什么问题?Fiber是什么?Fiber是如何解决问题的?Fiber用来解决什么问题? JavaScript引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行…...
【Linux】初学Linux你需要掌握这些基本指令(二)
目录 1.man指令 2.cp指令 3.mv指令 4.tree指令 5.echo指令 6.more指令 7.less指令(重要) 8.head与tail指令 9.date指令 显示时间常用参数: 设置时间常用参数: 10.cal指令 11.find & whereis & which指令 …...
Linux中VI/VIM 编辑器
1、概述所有Linux系统都会内置vi文本编辑器vim是vi的升级版,可以主动以字体颜色分辨语法的正确性,代码补完和编译,错误跳转等功能。2、vi和vim的三种模式基本上 vi/vim 共分为三种模式,分别是一般模式、编辑模式、命令模式2.1、一…...
PDF怎么转换成Word?两种PDF免费转Word方法推荐
不知道你们有没有发现,我们在网上下载的很多资料都是PDF格式的,尽管PDF文件也可以通过专门的PDF编辑器来编辑,但是PDF文档作为版式文档,编辑起来还是存在很多局限性,所有当我们需要大量编辑修改文档的时候,…...
极兔一面:Dockerfile如何优化?注意:千万不要只说减少层数
说在前面 在40岁老架构师 尼恩的读者交流群(50)中,面试题是一个非常、非常高频的交流话题。 最近,有小伙伴面试极兔时,遇到一个面试题: 如果优化 Dockerfile? 小伙伴没有回答好,只是提到了减少镜像层数。…...
SpringBoot+Vue实现酒店客房管理系统
文末获取源码 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏…...
自适应多因素认证:构建不可破解的企业安全防线|身份云研究院
打开本文意味着你理解信息安全的重要性,并且希望获取行业最佳实践来保护你所在组织的信息安全。本文将带你了解多因素认证(MFA:Multi-Factor-Authentication)对于企业信息安全的重要性以及实施方法。 多因素认证(MFA&…...
阶段二8_集合ArrayList_学生管理系统_详细步骤
一.学生管理系统案例 1.需求: 针对目前我们的所学内容,完成一个综合案例:学生管理系统! 该系统主要功能如下: 1.添加学生:通过键盘录入学生信息,添加到集合中 2.删除学生:通过键盘录…...
一篇解决Linux 中的负载高低和 CPU 开销并不完全对应
负载是查看 Linux 服务器运行状态时很常用的一个性能指标。在观察线上服务器运行状况的时候,我们也是经常把负载找出来看一看。在线上请求压力过大的时候,经常是也伴随着负载的飙高。 但是负载的原理你真的理解了吗?我来列举几个问题&#x…...
关于IDM下载器,提示:一个假冒的序列号被用来注册……idea项目文件路径报红
关于IDM下载器,提示:一个假冒的序列号被用来注册……到C:\Windows\System32\drivers\etc 修改目录下面的hosts文件(如果没有修改的权限就右键属性hosts文件修改user的权限为完全控制),在hosts里面增加以下内容…...
JVM - 高效并发
目录 Java内存模型和内存间的交互操作 Java内存模型 内存间的交互操作 内存间交互操作的规则 volatile特性 多线程中的可见性 volatile 指令重排原理和规则 指令重排 指令重排的基本规则 多线程中的有序性 线程安全处理 锁优化 锁优化之自旋锁与自适应自旋 锁优…...
中小学智慧校园电子班牌系统源码 Saas云平台模式
智慧电子班牌区别于传统电子班牌,智慧校园电子班牌系统更加注重老师和学生的沟通交流和及时数据交互。学校为每个教室配置一台智能电子班牌,一般安装于教室门口,用来实时显示学校通知、班级通知,可设置集中分布式管理,…...
记录一次服务器被攻击的经历
突然收到阿里云发过来的异常登陆的信息: 于是,急忙打开电脑查看对应的ECS服务器的记录: 发现服务器的cpu占用率异常飙升,所以可以大概断定服务器已经被非法入侵了。 通过自己的账号登陆后,发现sshd服务有异常的链接存…...
Python解题 - CSDN周赛第29期 - 争抢糖豆
本期问哥是志在必得,这本算法书我已经觊觎许久,而之前两次因为种种原因未能如愿。因此,问哥这几天花了不少时间,把所有之前在每日一练做过的题目重新梳理了一遍。苦心人,天不负,感谢官方大大! 第…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
