Linux(程序设计):61---定时机制之SIGALRM信号(附升序的定时器链表设计、定时器链表处理非活动连接)

一、SIGALRM信号概述

二、基于升序链表的定时器设计

  • 定时器设计:
    • 一个定时器至少包含两个成员(一个超时时间(相对时间或者绝对时间)和一个任务回调函数),有时还可能包含回调函数被执行时需要传入的参数,以及是否重启定时器等信息
    • 每个定时器还要包含指向下一个定时器的指针成员。如果链表是双向的,每个定时器还需要包含指向前一个定时器的指针成员

链表设计

  • struct client_data:用于表示客户端信息的结构体
  • class util_timer:定时器类,用于表示某个定时器
  • class sort_timer_list:定时器链表,按照定时器的时间长短进行升序排序
//sort_timer_list.h

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

#define BUFFER_SIZE 64

class util_timer; //声明定时器类

//客户端结构体
struct client_data
{
    struct sockaddr_in address; //客户端地址
    int sock_fd; //客户端套接字
    char recv_buf[BUFFER_SIZE]; //读缓存区
    util_timer* timer; //客户端所使用的定时器
};

//定时器类
class util_timer
{
public:
    util_timer():prev(NULL),next(NULL){} //构造函数
public:
    time_t expire; //任务的超时时间,此处使用绝对时间
    struct client_data* user_data; //客户端数据
    void (*cb_func)(struct client_data *user_data); //定时器的任务回调函数
    util_timer* prev; //前一节点指针
    util_timer* next; //后一节点指针
};

//定时器链表类(升序、双向链表、带有头尾节点)
class sort_timer_list
{
public:
    sort_timer_list():head(NULL),tail(NULL){}//构造函数
    ~sort_timer_list();//析构函数

    //将timer节点插入到链表合适的位置
    void add_timer(util_timer* timer);
    
    /*当某个定时任务发生变化时,调整定时器在链表中的位置。该函数只考虑被调整的
    定时器的超时时间延长的情况,所以只需要把定时器节点向链表的尾部移动*/
    void adjust_timer(util_timer* timer);

    //将目标定时器从链表中删除
    void del_timer(util_timer* timer);

    /*如果程序接收到SIGALRM信号,就在SIGALRM信号的处理函数中调用此函数,
      来判断链表中是否有定时器事件过期,从而来执行事件*/
    void tick();
private:
    //将timer对象添加到以list_head起始点的链表中
    void add_timer(util_timer* timer,util_timer* list_head);
private:
    util_timer* head; //链表头结点
    util_timer* tail; //链表尾结点
};

sort_timer_list::~sort_timer_list()
{
    util_timer* temp;
    //循环释放链表节点
    while(this->head)
    {
        temp=this->head->next;
        delete this->head;
        this->head=temp;
    }
}

//将timer节点插入到链表合适的位置
void sort_timer_list::add_timer(util_timer* timer)
{
    //插入的节点为空
    if(timer==NULL) 
        return;

    //链表为空
    if(this->head==NULL){ 
        this->head=this->tail=timer;
        return;
    }

    //链表不为空,比头节点的时候还小,插入到头节点s之前,并作为新头节点
    if(timer->expire<this->head->expire){
        this->head->prev=timer;
        timer->next=this->head;
        this->head=timer;
        return;
    }

    //否则,插入到链表中
    add_timer(timer,this->head);
}

/*当某个定时任务发生变化时,调整定时器在链表中的位置。该函数只考虑被调整的
    定时器的超时时间延长的情况,所以只需要把定时器节点向链表的尾部移动*/
void sort_timer_list::adjust_timer(util_timer* timer)
{
    //插入的节点为空
    if(timer==NULL) 
        return;

    util_timer* tmp=timer->next;

    //如果定时器节点原本为尾节点或仍比后一个节点的时间值小,那么位置不变
    if((tmp==NULL)||(timer->expire<tmp->expire)){
        return;
    }

    //如果目标定时器节点为头节点,那么从链表中取出并重新插入链表
    if(timer==this->head){
        this->head=this->head->next;
        this->head->prev=NULL;
        timer->next=NULL;
        add_timer(timer,this->head);
    }
    //如果目标定时器节点不是头节点,将其从链表中取出并插入到其所在位置的后面部分链表中
    else{
        timer->prev->next=timer->next;
        timer->next->prev=timer->prev;
        add_timer(timer,timer->next);
    }
}

//将目标定时器从链表中删除
void sort_timer_list::del_timer(util_timer* timer)
{
    //删除的节点为空
    if(timer==NULL) 
        return;

    //如果链表只有一个节点,并且删除的就是这个节点
    if((timer==this->head)&&(timer==this->tail))
    {
        delete timer;
        this->head=this->tail=NULL;
        return;
    }

    //如果链表只有两个节点,且删除的是头节点
    if(timer==this->head){
        this->head=this->tail;
        this->head->prev=NULL;
        delete timer;
        timer=NULL;
        return;
    }

    //如果链表只有两个节点,且删除的是尾节点
    if(timer==this->tail){
        this->tail=this->head;
        this->head->next=NULL;
        delete timer;
        timer=NULL;
        return;
    }

    //如果删除的节点在中间
    timer->next->prev=timer->prev;
    timer->prev->next=timer->next;
    delete timer;
    timer=NULL;
}

/*如果程序接收到SIGALRM信号,就在SIGALRM信号的处理函数中调用此函数,
 来判断链表中是否有定时器事件过期,从而来执行事件*/
void sort_timer_list::tick()
{
    //链表为空
    if(this->head==NULL)
        return;

    //获取当前时间
    time_t current_time=time(NULL);
    
    util_timer* tmp=this->head;
    while(tmp)
    {
        /*如果时间比定时器节点的时间小,说明未到执行时间,退出
        (因为为升序链表,所以判断一个,后面就不需要判断了)*/
        if(current_time<tmp->expire)
            break;

        //执行函数
        tmp->cb_func(tmp->user_data);

        //头结点向后移动
        head=tmp->next;
        if(this->head)
            this->head->prev=NULL;
        //删除原头节点
        delete tmp;
        tmp=this->head;
    }
}

//将timer对象添加到以list_head起始点的链表中
void sort_timer_list::add_timer(util_timer* timer,util_timer* list_head)
{
    util_timer* prev=list_head;
    util_timer* tmp=prev->next;
    
    //循环遍历
    while(tmp)
    {
        if(timer->expire<tmp->expire){//如果新节点比后一节点的时间值小
            prev->next=timer;
            timer->next=tmp;
            tmp->prev=timer;
            timer->prev=prev;
            break;
        }
        prev=tmp;
        tmp=tmp->next;
    }

    //如果遍历完还没找到合适的插入点,说明插入到比所有的节点时间值都大,那么作为尾节点插入
    if(tmp==NULL){
        //prev此时为尾节点
        prev->next=timer;
        timer->prev=prev;
        timer->next=NULL;
        this->tail=timer;
    }
}

链表测试

//list_timer.cpp

#include "sort_timer_list.h"

//客户端执行函数
void client_fun(struct client_data *user_data);

void sig_func(int signo); //信号处理函数

sort_timer_list *myList=new sort_timer_list();

int main(int argc,char* argv[])
{
    //创建两个客户端
    struct client_data client1,client2;
    bzero(&client1,sizeof(client1));
    bzero(&client1,sizeof(client1));

    time_t current_time=time(NULL);

    //创建定时器1
    util_timer *timer1=new util_timer();
    timer1->expire=current_time+10;
    timer1->cb_func=client_fun;
    timer1->user_data=&client1;
    timer1->user_data->timer=timer1;
    bcopy("I am client1",timer1->user_data->recv_buf,13);

    //创建定时器2
    util_timer *timer2=new util_timer();
    timer2->expire=current_time+25;
    timer2->cb_func=client_fun;
    timer2->user_data=&client2;
    timer2->user_data->timer=timer2;
    bcopy("I am client2",timer2->user_data->recv_buf,13);

    //将两个定时器加入链表中
    myList->add_timer(timer1);
    myList->add_timer(timer2);

    //为SIGALRM信号绑定处理函数
    signal(SIGALRM,sig_func);

    //设置一个struct itimerval定时器
    struct itimerval tick;
    tick.it_value.tv_sec = 20;  //struct itimerval定时器首次在20秒之后到期启动
    tick.it_value.tv_usec = 0;
    tick.it_interval.tv_sec  =10; //struct itimerval定时器第一次到时之后,每隔10秒到期一次
    tick.it_interval.tv_usec = 0;
 
    //开启一个struct itimerval定时器,种类为ITIMER_REAL,触发的信号为SIGALRM
    int ret = setitimer(ITIMER_REAL, &tick, NULL);
 
    if ( ret != 0)
    {
        printf("setitimer error:%s \n", strerror(errno) );
        exit(EXIT_FAILURE);
    }

    //pause:进程挂起,在接收信号,并从信号处理函数中返回时才取消挂起
    while(1)
        pause();
    
    exit(EXIT_SUCCESS);
}

void client_fun(struct client_data *user_data)
{
    printf("%s\n",user_data->recv_buf);
}

void sig_func(int signo)
{
    //在SIGALRM信号处理函数中轮询链表是否有定时任务到期可以执行
    myList->tick();
}
  • 演示效果如下:可以看到客户端1和客户端2的定时器都执行了

三、处理非活动连接

  • 服务器程序通常要定期处理非活动连接:给客户端发送一个重连请求、或者关闭该连接、或者其他等等
  • 处理非活动连接的方式:

编码设计

  • 我们利用alarm函数周期性地触发SIGALRM信号,在该信号处理函数中利用管道通知主循环执行定时器链表上的定时任务——关闭非活动连接

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值