Socket心跳包机制总结
跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。
在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。
心跳包一般来说都是在逻辑层发送空的echo包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。
其实,要判定掉线,只需要send或者recv一下,如果结果为零,则为掉线。但是,在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候,就需要我们的心跳包了,用于维持长连接,保活。
在获知了断线之后,服务器逻辑可能需要做一些事情,比如断线后的数据清理呀,重新连接呀……当然,这个自然是要由逻辑层根据需求去做了。
总的来说,心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒。
心跳检测步骤:
1 客户端每隔一个时间间隔发生一个探测包给服务器
2 客户端发包时启动一个超时定时器
3 服务器端接收到检测包,应该回应一个包
4 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
5 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。
心跳包一般来说都是在逻辑层发送空的echo包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。
其实,要判定掉线,只需要send或者recv一下,如果结果为零,则为掉线。但是,在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候,就需要我们的心跳包了,用于维持长连接,保活。
在获知了断线之后,服务器逻辑可能需要做一些事情,比如断线后的数据清理呀,重新连接呀……当然,这个自然是要由逻辑层根据需求去做了。
总的来说,心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒。
心跳检测步骤:
1 客户端每隔一个时间间隔发生一个探测包给服务器
2 客户端发包时启动一个超时定时器
3 服务器端接收到检测包,应该回应一个包
4 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
5 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
很多人会用boolean socketFlag = socket.isConnected() && socket.isClosed()来判断就行了,但事实上这些方法都是访问socket在内存驻留的状态,当socket和服务器端建立链接后,即使socket链接断掉了,调用上面的方法返回的仍然是链接时的状态,而不是socket的实时链接状态,所以这样心跳用这个不靠谱,下面给出例子证明这一点。
服务器端:
- package com.csc.server;
- import java.net.*;
- /**
- * @description 从这里启动一个服务端监听某个端口
- * @author csc
- */
- public class DstService {
- public static void main(String[] args) {
- try {
- // 启动监听端口 30000
- ServerSocket ss = new ServerSocket(30000);
- // 没有连接这个方法就一直堵塞
- Socket s = ss.accept();
- // 将请求指定一个线程去执行
- new Thread(new DstServiceImpl(s)).start();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- package com.csc.server;
- import java.net.Socket;
- /**
- * @description 服务的启动的线程类
- * @author csc
- */
- public class DstServiceImpl implements Runnable {
- Socket socket = null;
- public DstServiceImpl(Socket s) {
- this.socket = s;
- }
- public void run() {
- try {
- int index = 1;
- while (true) {
- // 5秒后中断连接
- if (index > 10) {
- socket.close();
- System.out.println("服务端已经关闭链接!");
- break;
- }
- index++;
- Thread.sleep(1 * 1000);//程序睡眠1秒钟
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- package com.csc.client;
- import java.net.*;
- /**
- * @description 客户端打印链接状态
- * @author csc
- */
- public class DstClient {
- public static void main(String[] args) {
- try {
- Socket socket = new Socket("127.0.0.1", 30000);
- socket.setKeepAlive(true);
- socket.setSoTimeout(10);
- while (true) {
- System.out.println(socket.isBound());
- System.out.println(socket.isClosed());
- System.out.println(socket.isConnected());
- System.out.println(socket.isInputShutdown());
- System.out.println(socket.isOutputShutdown());
- System.out.println("------------我是分割线------------");
- Thread.sleep(3 * 1000);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- true
- false
- true
- false
- false
- ------------我是分割线------------
其实,想要判断socket是否仍是链接状态,只要发一个心跳包就行了,如下一句代码:
- socket.sendUrgentData(0xFF); // 发送心跳包
既然找到了方法,我们就在测试一下,服务端代码无需改动,客户端代码如下:
- package com.csc.client;
- import java.net.*;
- /**
- * @description 客户端打印链接状态
- * @author csc
- */
- public class DstClient {
- public static void main(String[] args) {
- try {
- Socket socket = new Socket("127.0.0.1", 30000);
- socket.setKeepAlive(true);
- socket.setSoTimeout(10);
- while (true) {
- socket.sendUrgentData(0xFF); // 发送心跳包
- System.out.println("目前处于链接状态!");
- Thread.sleep(3 * 1000);//线程睡眠3秒
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
重新运行客户端程序,看到控制台打印如下信息:
服务端程序运行10秒后再当执行“socket.sendUrgentData(0xFF);”语句时,socket链接断开了,所以会抛出异常。
另外注意,心跳包只是用来检测socket的链接状态,并不会作为socket链接的通信内容,这点应当注意。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?