网络超时检测、心跳检测的方法

在网络通信中很多操作都是默认阻塞的,比如 recv函数,当接收缓冲区中的数据没有达到水位线时,上层会一直处在阻塞等待数据就绪的状态。出现这种情况的原因有两个,一个是数据没有就绪,一个是网络连接断开。

  • 超时检测:当数据没有就绪时,避免当前进程在某个位置无限制的阻塞
  • 心跳检测:定时检查网络连接是否断开

        目录

1、网络超时检测

(1) 设置套接字属性

(2) 通过select模型检测

(3) 信号检测

2、心跳检测

(1) 定期发送检测报文

(2) 设置套接字属性


1、网络超时检测

(1) 设置套接字属性

可以使用 setsockopt 函数来设置套接字属性,setsockopt函数的第三个参数有一个 SO_RCVTIMEO 选项来设置接收超时时间。

struct timeval tout;
tout.tv_sec = 5;
tout.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(struct timeval));

recv();        // 从接收缓冲区读取数据

(2) 通过select模型检测

select模型是五种IO模型中的一种,它会逐个遍历每一个文件描述符,并判断文件描述符是否读就绪或者写就绪。

struct fd_set rdfs;
struct timeval tv = {5, 0};    // 设置select模型阻塞等待时间

FD_ZERO(&rdfs);
FD_SET(sockfd, &rdfs);

if(select(sockfd+1, &rdfs, NULL, NULL, &tv) > 0){    
    recv();            // 从接收缓冲区读取数据
}

(3) 信号检测

我们可以设置定时器,每隔一定时间向当前进程发送 SIGALRM 信号,此时我们的进程需要做如下两件事。

  • 使用sigaction函数捕获该信号
  • 设置sigaction结构体中的 sa_handler 成员为 SA_RESTART。SA_RESTART的作用是,捕获到信号时,如果当前进程有系统调用处在阻塞状态,那么就会重新执行系统调用。
void sigHandler(int signum){
    return;
}

int main(){
    
    // 先获取到原本的信号处理配置
    struct sigaction act;
    sigaction(SIGALRM, NULL, &act);

    // 设置新的信号处理配置
    act.sa_handler = sigHandler;
    act.sa_flags |= SA_RESTART;
    sigaction(SIGALRM, &act, NULL);

    // 设置定时器,每隔5s发送一次 SIGALRM 信号
    alarm(5);           
    
    // 从缓冲区中读取数据
    recv();        

}

2、心跳检测

(1) 定期发送检测报文

以客户端和服务端的交互为例。如果服务端在一段时间里没有收到对方发来的报文,此时服务端可以发送一个检测报文(数据可以是空的)。

  • 如果对方在设定次数内或者指定时间内,给出了应答,说明对方在线
  • 如果对方在指定时间里依旧没有应答,说明对方断开连接了。

(2) 设置套接字属性

setsockopt函数的第三个参数有一个可选值为SO_KEEPALIVE,表示保持连接,设置该选项以后,当前进程会每隔2个小时检测一下连接是否断开,因为2个小时的时间间隔太长了,所以我们还需要设置其他选项来控制检测时间间隔。

setsockopt的第二个参数setsockopt的第三个参数含义
SOL_SOCKETSO_KEEPALIVE设置套接字处于保持连接的状态,每隔一定时间检测连接是否断开
SOL_TCPTCP_KEEPIDLE从设置当前选项起,多长时间以后,第一次检测连接状态
SOL_TCPTCP_KEEPINTVL自第一次检测以后,每隔多长时间检测一次连接状态
SOL_TCPTCP_KEEPCNT

判定连接断开时,KeepAlive的检测次数。比如设为5,表示连续5次检测连接状态都是断开的,此时判定连接断开

下面是封装好的心跳检测函数: 

/**
 * 通过设置套接字属性来定期检测连接状态
 * 
 * @param sockfd    套接字
 * @param attr_on   套接字属性的值(1 表示设置保持连接;0表示不保持连接)
 * @param idle_time 多长时间以后开始第一次连接状态检测
 * @param interval  自第一次连接状态检测以后,每隔多长时间检测一次
 * @param cnt       检测多少次才判定连接断开
 */
void setKeepAlive(int sockfd, 
                  int attr_on, 
                  socklen_t idle_time, 
                  socklen_t interval, 
                  socklen_t cnt);

使用时注意传入的参数

int keepAlive = 1;        // 设定是否保持连接
int keepIdle = 5;         // 多长时间以后开始第一次检测
int keepInterval = 5;     // 自第一次检测以后,每隔多长时间检测一次
int keepCount = 3;        // 判定连接断开的检测次数

setKeepAlive(sockfd, keepAlive, keepIdle, keepInterval, keepCount);
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值