心跳机制原理学习

news2025/2/28 4:02:17

心跳机制

应用场景:

在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候,就需要我们的心跳包了,用于维持长连接,保活

什么是心跳机制?

就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开。

发包方:可以是客户也可以是服务端,看哪边实现方便合理。
心跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒。

心跳包的发送,通常有两种技术:

1.应用层自己实现的心跳包

由应用程序自己发送心跳包来检测连接是否正常,服务器每隔一定时间向客户端发送一个短小的数据包,然后启动一个线程,在线程中不断检测客户端的回应, 如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线;同样,如果客户端在一定时间内没有收到服务器的心跳包,则认为连接不可用。

2.使用SO_KEEPALIVE套接字选项

在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项. 不论是服务端还是客户端,一方开启KeepAlive功能后,就会自动在规定时间内向对方发送心跳包, 而另一方在收到心跳包后就会自动回复,以告诉对方我仍然在线。因为开启KeepAlive功能需要消耗额外的宽带和流量,所以TCP协议层默认并不开启默认的KeepAlive超时需要7,200,000 MilliSeconds, 即2小时,探测次数为5次。对于很多服务端应用程序来说,2小时的空闲时间太长。因此,我们需要手工开启KeepAlive功能并设置合理的KeepAlive参数

开启KeepAlive选项后会导致的三种情况:

1、对方接收一切正常:以期望的ACK响应,2小时后,TCP将发出另一个探测分节
2、对方已崩溃且已重新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接口本身则被关闭。
3、对方无任何响应:套接口的待处理错误被置为ETIMEOUT,套接口本身则被关闭.

有关SO_KEEPALIVE的三个参数:
1.tcp_keepalive_intvl,保活探测消息的发送频率。默认值为75s。
发送频率tcp_keepalive_intvl乘以发送次数tcp_keepalive_probes,就得到了从开始探测直到放弃探测确定连接断开的时间,大约为11min。
2.tcp_keepalive_probes,TCP发送保活探测消息以确定连接是否已断开的次数。默认值为9(次)。
3.tcp_keepalive_time,在TCP保活打开的情况下,最后一次数据交换到TCP发送第一个保活探测消息的时间,即允许的持续空闲时间。默认值为7200s(2h)。

总结:

一个服务器通常会连接多个客户端,因此由用户在应用层自己实现心跳包,代码较多 且稍显复杂。用TCP/IP协议层为内置的KeepAlive功能来实现心跳功能则简单得多。心跳包在按流量计费的环境下增加了费用.但TCP得在连接闲置2小时后才发送一个保持存活探测段,所以通常的方法是将保持存活参数改小,但这些参数按照内核去维护,而不是按照每个套接字维护,因此改动它们会影响所有开启该选项的套接字。

下面我们通过一个实例来展示心跳机制。

结构,一个客户程序,和一个服务程序。

步骤:
服务器:
1.经过socket、bind、listen、后用accept获取一个客户的连接请求,为了简单直观,这里服务器程序只接收一个connect请求,我们用clifd来获取唯一的一个连接。
2.为clifd修改KeepAlive的相关参数,并开启KeepAlive套接字选项,这里我们把间隔时间设为了5秒,闲置时间设置了5秒,探测次数设置为5次。
3.将clifd加入select监听的描述符号集

客户:很简单,只是连接上去,并停留在while死循环。

方式:
服务程序放到阿里云服务器上,我们执行服务程序并将输出结果重定向到一个日志文件,目的是为了将我们本地网络连接断开后,超过了keepalive闲置时间+重复发包探测的时间后,重新打开本地的网络连接,并登录服务器,通过该日志文件的内容来查看程序的打印结果。

客户:

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
using namespace std;

int main()
{
	int skfd;
	if ((skfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("");
		exit(-1);
	}		

struct sockaddr_in saddr;
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(9999);
saddr.sin_addr.s_addr = inet_addr("115.29.109.198");

if (connect(skfd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) {
	perror("");
	exit(-1);
}
 
cout << "连接成功" << endl;
while(1);
return 0;
}

服务器

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <sys/select.h>
#include <netinet/tcp.h>
using namespace std;

#define LISTENNUM 5

int main()
{
	int skfd;
    if ((skfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    	perror("");
        exit(-1);
    }

struct sockaddr_in saddr;
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(9999);
saddr.sin_addr.s_addr = inet_addr("115.29.109.198");
 
if (bind(skfd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) {
	perror("");
    exit(-1);
}
 
if (listen(skfd, LISTENNUM) < 0) {
	perror("");
    exit(-1);
}
    
int clifd;
if ((clifd = accept(skfd, NULL, NULL)) < 0) {
	perror("");
    exit(-1);
}
cout << "有新连接" << endl;

//setsockopt
int tcp_keepalive_intvl = 5;   //保活探测消息的发送频率。默认值为75s
int tcp_keepalive_probes = 5;  //TCP发送保活探测消息以确定连接是否已断开的次数。默认值为9次
int tcp_keepalive_time = 5;    //允许的持续空闲时间。默认值为7200s(2h)
int tcp_keepalive_on = 1;
 
if (setsockopt(clifd, SOL_TCP, TCP_KEEPINTVL,
    &tcp_keepalive_intvl, sizeof(tcp_keepalive_intvl)) < 0) {
    perror("");
    exit(-1);
}
 
if (setsockopt(clifd, SOL_TCP, TCP_KEEPCNT,
	&tcp_keepalive_probes, sizeof(tcp_keepalive_probes)) < 0) {
    perror("");
    exit(-1);
}
 
if (setsockopt(clifd, SOL_TCP, TCP_KEEPIDLE,
    &tcp_keepalive_time, sizeof(tcp_keepalive_time)) < 0) {
	perror("");
    exit(-1);
}
 
if (setsockopt(clifd, SOL_SOCKET, SO_KEEPALIVE,
    &tcp_keepalive_on, sizeof(tcp_keepalive_on))) {
    perror("");
    exit(-1);
}
 
char buf[1025];
int r;
int maxfd;
fd_set rset;
FD_ZERO(&rset);
sleep(5);
while (1) {
	FD_SET(clifd, &rset);
    maxfd = clifd + 1;
    if (select(maxfd, &rset, NULL, NULL, NULL) < 0) {
    	perror("");
        exit(-1);
    }
            
    if (FD_ISSET(clifd, &rset)) {
    	r = read(clifd, buf, sizeof(buf));
        if (r == 0) {
        	cout << "接收到FIN" << endl;
            close(clifd);
            break;
        }
        else if (r == -1) {
        	if (errno == EINTR) {
            	cout << "errno: EINTR" << endl;
                continue;
            }
 
           	if (errno == ECONNRESET) {
            	cout << "errno: ECONNRESET" << endl;
                cout << "对端已崩溃且已重新启动" << endl;
                close(clifd);
                break;
            }
                            
            if (errno == ETIMEDOUT) {
            	cout << "errno: ETIMEDOUT" << endl;
               	cout << "对端主机崩溃" << endl;
                close(clifd);
                break;
            }
 
            if (errno == EHOSTUNREACH) {
            	cout << "errno: EHOSTUNREACH" << endl;
                cout << "对端主机不可达" << endl;
               	close(clifd);
                break;
            }
        }
    }
}
 
close(skfd);
return 0;
}

执行服务程序并重定向到日志文件server.log,执行客户程序,之后将网络连接断开
一段时间后(大于KeepAlive空闲时间+重复探测时间),重新打开网络连接,用ssh登录服务器,查看server.log文件.发现打印了ETIMEDOUT
,验证了在客户网络断开后,到达空闲时间时,服务器由于开启了KeepAlive选项,会向客户端发送探测包,几次还没收到客户端的回应,那么select将返回套接字可读的条件,并且read返回-1.设置相关错误,

而与之相反的情况是如果不开启KeelAlive选项,那么即使客户端网络断开超过了整个的空闲和探测时间,服务端的select也不会返回可读的条件,即应用程序无法得到通知。

本节思维导图

image-20240409092122057

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

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

相关文章

实验5 流程图和盒图ns图

一、实验目的 通过绘制流程图和盒图&#xff0c;熟练掌握流程图和盒图的基本原理。 能对简单问题进行流程图和盒图的分析&#xff0c;独立地完成流程图和盒图设计。 二、实验项目内容&#xff08;实验题目&#xff09; 1、用Microsoft Visio绘制下列程序的程序流程图。 若…

Go语言mac环境搭建详解

Go语言mac环境搭建详解见视频&#xff0c;视频下方也有讲解具体的操作步骤。 Golang Mac电脑环境搭建、开发工具Vscode配置 Go语言mac环境搭建步骤如下&#xff1a; 1、下载安装Golang Go官网下载地址&#xff1a;https://golang.org/dl/ Go官方镜像站&#xff08;推荐&…

一线城市嵌入式软件开发工程师每年调薪幅度能有多少?

15%对于刚入职的新人来说确实有可能有&#xff0c;如果你的底薪比较低的话。不过后面随着你工资变高&#xff0c;这个幅度会越来越趋向0%。在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「 嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在…

GEE数据集——汉森全球森林变化数据集Hansen Global Forest Change v1.11 (2000-2023)

Hansen Global Forest Change v1.11 (2000-2023) 对大地遥感卫星图像进行时间序列分析以确定全球森林范围和变化特征的结果。 第一个 "和 "最后一个 "波段是大地遥感卫星光谱波段&#xff08;红、近红外、SWIR1 和 SWIR2&#xff09;的第一个和最后一个可用年…

python镜像安装(ios、windows)

如果你在使用Python时发现官方网站下载速度过慢&#xff0c;可以考虑使用国内的Python镜像源下载Python。国内的Python镜像源可以提供更快的下载速度和更好的下载体验。 以下是使用国内Python镜像源下载Python的步骤&#xff1a; 步骤 1&#xff1a;选择Python版本 首先&…

知识图谱的挑战与机遇

知识图谱的挑战与机遇 引言 欢迎来到知识图谱的世界&#xff0c;这是一片未被充分挖掘的宝地&#xff0c;充满了挑战与机遇。本篇文章旨在深入探讨知识图谱领域面临的主要挑战&#xff0c;并预测未来可能的发展机遇。知识图谱以其独特的方式整合和呈现信息&#xff0c;已经成为…

Linux进阶篇:firewalld详解——firewalld 的概念作用以及如何使用

Linux firewalld详解——firewalld 的概念&作用以及如何使用 在这篇文章中&#xff0c;我们将详细介绍Linux系统中的firewalld&#xff0c;它是一款强大的防火墙管理工具。我们将介绍firewalld的基本概念和作用&#xff0c;并通过实例演示如何使用它来保护您的系统。 一、…

VMware虚拟机的安装指南

前言 在数字化时代&#xff0c;虚拟机技术为我们提供了无限可能&#xff0c;让一台计算机同时运行多个操作系统成为现实。VMware作为知名的虚拟化解决方案之一&#xff0c;其强大的功能和易用性备受推崇。本文将详细介绍如何安装VMware虚拟机&#xff0c;让您轻松进入虚拟化的…

嵌入式需要模电数电的哪些内容?

1 数电知识 1.1 布尔代数和逻辑门 了解布尔代数的基本概念和逻辑门的工作原理&#xff0c;包括与门、或门、非门、与非门、或非门、异或门等。 1.2 时序逻辑和时钟信号在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「 嵌入式的资料从专业入门到高级教…

搭建第一个Web服务器(在eclipse或idea上部署Tomcat服务器)

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

客户关系CRM管理系统源码 企业crm管理系统

客户关系CRM管理系统源码 企业crm管理系统 系统功能介绍 1、 公海管理&#xff1a;公海类型、客户公海。 2、 线索管理&#xff1a;我的线索、线索列表、线索状态、线索来源。 3、 客户管理&#xff1a;我的客户、客户列表、成交客户、行业类别、预查、地区列表、客户状态、…

抗酸碱耐高温小型PFA溶样瓶可搭配电热板用于样品前处理实验

PFA溶样罐&#xff0c;也叫PFA管形瓶、可溶性聚四氟乙烯溶样罐、消解瓶等&#xff0c;常用于地质地矿、地球化学、土壤微生物等样品分析消解实验&#xff0c;可搭配石墨消解仪、电热板使用。广泛适用于痕量分析、环境监测、重金属检测、半导体、新材料、新能源等。 规格参考&a…

数据仓库的ELT/ETL

ETL 和 ELT 有很多共同点&#xff0c;从本质上讲&#xff0c;每种集成方法都可以将数据从源端抽取到数据仓库中&#xff0c;两者的区别在于数据在哪里进行转换。 01 ETL ETL – 抽取、转换、加载 从不同的数据源抽取信息&#xff0c;将其转换为根据业务定义的格式&#xff0…

集群服务器使用

查看剩余资源&#xff1a;sinfo -O Nodehost,Gres:.30,GresUsed:.45 第二列是总资源 第三列是占用量 申请资源&#xff1a;salloc -N 1 -n 1 -p normal --gresgpu:NVIDIAGeForceGTX1080Ti1 gres的名字来源于sinfo 查看任务情况 squeue JOBID NODES 连接资源 ssh NODES …

The C programming language (second edition,KR) exercise(CHAPTER 3)

E x c e r c i s e 3 − 1 Excercise\quad 3-1 Excercise3−1&#xff1a;输出结果如图1所示&#xff0c;这里故意让二分搜索算法去寻找一个在数组中不存在在的数&#xff0c;然后去看两种二分搜索算法分别所花费的时间的大小&#xff0c;为了使得所花费的时间更具有可分辨性&a…

curl: (60) Peer‘s Certificate issuer is not recognized curl请求报错

此种情况多发生在自签名的证书或者证书和域名不对&#xff0c;报错含义是签发证书机构未经认证&#xff0c;无法识别。解决办法就是替换证书&#xff08;补充证书机构&#xff09;文件就好&#xff0c;如果没有可用的证书可以去Gworg申请一个。

记录我第一场面了40min+的面试

中冶赛迪信息技术(重庆)有限公司 国企 首先3/24投递的&#xff0c;4/10打了电话问是否接受劳务派遣&#xff0c;我当时不知道劳务派遣什么意思&#xff0c;问了和售前售后是不是类似&#xff0c;得到了不大一样的回答&#xff0c;后面加了微信&#xff0c;定了11开始面试。 这…

【计算机考研】数据结构都不会,没有思路,怎么办?

基础阶段&#xff0c;并不需要过于专门地练习算法。重点应该放在对各种数据结构原理的深入理解上&#xff0c;也可以说先学会做选择题、应用题。 因为在考试中&#xff0c;大部分的算法题目&#xff0c;尤其是大题&#xff0c;往往可以通过简单的暴力解决方案得到较高的分数。…

【Java8新特性】四、强大的Stream api

​ 这里写自定义目录标题 一、了解Stream二、流(stream)到底是什么&#xff1f;三、Stream操作的三个步骤四、创建Stream的四种方式五、Stream 的中间操作1、筛选和切片2、map 映射3、排序 六、Stream 的终止操作1、查找和匹配2、归约3、收集 一、了解Stream Stream是Java8中…

【Locust分布式压力测试】

Locust分布式压力测试 https://docs.locust.io/en/stable/running-distributed.html Distributed load generation A single process running Locust can simulate a reasonably high throughput. For a simple test plan and small payloads it can make more than a thousan…