基于升序链表的定时器(https://blog.youkuaiyun.com/destory27/article/details/81748580)和时间轮定时器(https://blog.youkuaiyun.com/destory27/article/details/81750790)都是以固定的频率调用tick,并在其中依次检测到期的定时器,然而执行到定时器上的回调函数.设计定时器的另一种思路是:将所有的定时器中超过时间最小的一个定时器的超时值作为时间间隔.这样,一旦函数tick被调用,超时时间最小的定时器必然到期,就可以在tick函数中处理该定时器.然后,再次从剩余的定时器中找到超时时间最小的一个,并将这段最小时间设置为下一次时间间隔. -----------最小堆适合此方案。
时间堆定时器:
#ifndef _MIN_HEAP
#define _MIN_HEAP
//时间堆
#include <iostream>
#include <netinet/in.h>
#include <time.h>
using std::exception;
#define BUFFER_SIZE 64
class heap_timer; //前向声明
//绑定定时器和socket
struct client_data{
struct sockaddr_in address;
int sockfd;
char buf[BUFFER_SIZE];
heap_timer *timer;
};
/***********定时器类****************/
class heap_timer{
public:
heap_timer(int delay)
{
expire = time(NULL) + 3 * delay;
}
void (*cb_func)(client_data *); //回调函数
public:
time_t expire; //定时器生效绝对时间
client_data *user_data; //用户数据
};
/****************时间堆**********************/
class time_heap{
public:
time_heap(int cap)throw(std::exception); //初始化一个大小为cap的空堆
time_heap(heap_timer **init_array, int size, int capacity)throw(std::exception); //用已有数组初始化堆
~time_heap(); //销毁时间堆
void add_timer(heap_timer *timer)throw (std::exception); //添加目标定时器
void del_timer(heap_timer *timer); //删除目标定时器
heap_timer *top() const; //获得堆顶定时器
void pop_timer(); //删除堆顶
void tick(); //定时处理函数
bool empty()const; //是否空
private:
void percolate_down(int hole); //调整第hole个结点为子树拥有最小堆性质
void resize()throw (std::exception); //堆数组容量扩大一倍
private:
heap_timer **array; //堆数组
int capacity; //容量
int cur_size; //当前包含元素个数
};
//创建堆数组
time_heap::time_heap(int cap) throw(std::exception)
:capacity(cap),
cur_size(0)
{
array = new heap_timer*[capacity];
if(!array)
throw std::exception();
for(int i = 0; i < capacity; ++i)
array[i] = nullptr;
}
//用已有数组初始化堆
time_heap::time_heap(heap_timer **init_array, int size, int capacity)throw(std::exception)
:cur_size(size),
capacity(capacity)
{
if(capacity < size)
throw std::exception();
array = new heap_timer*[capacity];
if(array)
throw std::exception();
for(int i = 0; i < capacity; ++i)
array[i] = nullptr;
if(size)
{
//初始化
for(int i = 0; i < size; ++i)
array[i] = init_array[i];
//初建堆过程(找到最后一个非叶子结点,使以该结点为跟结点的子树满足最小堆)
for(int i = (cur_size - 1)/ 2; i > 0; --i)
percolate_down(i);
}
}
time_heap::~time_heap()
{
for(int i = 0; i < cur_size; ++i)
delete array[i];
delete []array;
}
//添加目标定时器
void time_heap::add_timer(heap_timer *timer) throw (std::exception)
{
if(!timer)
return ;
if(cur_size >= capacity) //当前堆数组容量不够 扩容
resize();
int hole = cur_size++; //新插入一个元素 hole是新建元素的位置
int parent = 0;
//调整新插入结点 使其满足最小堆
for(; hole > 0; hole = parent)
{
parent = (hole-1) / 2; //其父结点
if(array[parent]->expire <= timer->expire)
break;
array[hole] = array[parent];
}
array[hole] = timer;
return;
}
//删除目标定时器
void time_heap::del_timer(heap_timer *timer)
{
if(!timer)
return ;
timer->cb_func = nullptr;
}
//获得堆顶定时器
heap_timer* time_heap::top()const
{
if(empty())
return nullptr;
return array[0];
}
//删除堆顶
void time_heap::pop_timer()
{
if(empty())
return;
if(array[0])
{
delete array[0];
array[0] = array[--cur_size];
percolate_down(0); //调整
}
return;
}
void time_heap::tick()
{
heap_timer *tmp = array[0];
time_t cur = time(NULL); //循环处理堆中到期的定时器
while(!empty()) //
{
if(!tmp)
break;
if(tmp->expire > cur)
break;
if(array[0]->cb_func) //删除 执行堆顶定时器的任务
array[0]->cb_func(array[0]->user_data);
pop_timer();
tmp = array[0];
}
return;
}
//
bool time_heap::empty()const
{
return cur_size == 0;
}
void time_heap::percolate_down(int hole)
{
heap_timer *tmp = array[hole];
int child = 0;
for(; ((hole * 2 + 1) <= (cur_size - 1)); hole = child)
{
child = hole*2 + 1; //结点编号从0还是 所以+1到左孩子 //从左孩子右孩子选出最小的结点
if((child < cur_size - 1) && (array[child + 1]->expire < array[child]->expire))
++child;
if(array[child]->expire < tmp->expire)
array[hole] = array[child];
else
break;
}
array[hole] = tmp; //实现交换
return;
}
void time_heap::resize() throw (std::exception)
{
heap_timer **tmp = new heap_timer*[2 * capacity];
for(int i = 0; i < 2 * capacity; ++i)
tmp[i] = nullptr;
if(!tmp)
throw std::exception();
capacity = 2 * capacity;
for(int i =0; i < cur_size; ++i)
tmp[i] = array[i];
delete []array;
array = tmp;
}
#endif
服务器测试程序:(EPOLL)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <assert.h>
#include <signal.h>
#include <string.h>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include "MIN_HEAP.hpp"
#define FD_LIMIT 65535
#define MAX_EVENT_NUMBER 1024
#define TIMESLOT 1
static int pipefd[2];
static time_heap th{32};
static int epollfd = 0;
//设置非阻塞
int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
}
void addfd(int epollfd, int fd)
{
struct epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET; //ET模式
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event); //添加到事件列表
setnonblocking(fd); //设置非阻塞
return ;
}
void sig_handler(int sig)
{
int save_errno = errno;
int msg = sig;
send(pipefd[1], (char*)&msg, 1, 0);
errno = save_errno;
}
//信号处理
void addsig(int sig)
{
struct sigaction sa;
(struct sigaction *)memset(&sa, '\0', sizeof(struct sigaction));
sa.sa_handler = sig_handler;
sa.sa_flags |= SA_RESTART;
sigfillset(&sa.sa_mask);
assert(sigaction(sig, &sa, NULL) != -1);
return ;
}
//
void timer_handler()
{
th.tick();
heap_timer *tmp = th.top();
if(!tmp)
{
alarm(TIMESLOT); //修改定时时间
return;
}
int tim = tmp->expire - time(NULL);
alarm(tim); //修改定时时间
}
//回调函数
void cb_func(client_data *user_data)
{
epoll_ctl(epollfd, EPOLL_CTL_DEL, user_data->sockfd, 0);
assert(user_data);
close(user_data->sockfd);
std::cout << "close fd " << user_data->sockfd << std::endl;
}
int main(int argc, char **argv)
{
if(argc <= 2) //使用IP port
{
std::cout << "usage:" << argv[0] << " ipaddr port" << std::endl;
return -1;
}
struct sockaddr_in address;
bzero(&address, sizeof(struct sockaddr_in));
address.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &address.sin_addr);
address.sin_port = htons(atoi(argv[2]));
//socket
int listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd != -1);
int reuse = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
//bind
int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(struct sockaddr_in));
assert(ret != -1);
//listen
ret = listen(listenfd, 5);
assert(ret != -1);
//事件集合
struct epoll_event events[MAX_EVENT_NUMBER];
//列表套接字
int epollfd = epoll_create(5);
assert(epollfd != -1);
addfd(epollfd, listenfd); //添加到事件列表
//双向通信
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
assert(ret != -1);
setnonblocking(pipefd[1]);
addfd(epollfd, pipefd[0]);
//设置信号处理函数
addsig(SIGALRM);
addsig(SIGTERM);
bool stop_server = false;
client_data *users = new client_data[FD_LIMIT];
bool timeout = false;
alarm(TIMESLOT); //定时
while(!stop_server)
{
int num = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
if(num < 0 && errno != EINTR)
{
std::cout << "epoll failure" << std::endl;
break;
}
for(int i = 0; i < num; ++i)
{
int sockfd = events[i].data.fd;
if(sockfd == listenfd) //新的连接请求
{
struct sockaddr_in client;
socklen_t len = sizeof(struct sockaddr_in);
int connfd = accept(listenfd, (struct sockaddr*)&client, &len);
assert(connfd != -1);
addfd(epollfd, connfd);
users[connfd].address = client;
users[connfd].sockfd = connfd;
//创建定时器
heap_timer *timer = new heap_timer(2);
timer->user_data = &users[connfd];
timer->cb_func = cb_func; //回调函数
users[connfd].timer = timer;
th.add_timer(timer);
break;
}
else if(sockfd == pipefd[0] && events[i].events & EPOLLIN) //处理信号
{
int sig;
char signals[1024];
ret = recv(pipefd[0], signals, sizeof(signals), 0);
assert(ret != -1);
if(ret == 0)
continue;
else
{
for(int i = 0; i < ret; ++i)
switch(signals[i])
{
case SIGALRM:
timeout = true; //有定时任务需要处理
break;
case SIGTERM:
stop_server = true;
}
}
}
else if(events[i].events & EPOLLIN) //处理客户连接上接收的数据
{
memset(users[sockfd].buf, '\0', BUFFER_SIZE);
ret = recv(sockfd, users[sockfd].buf, BUFFER_SIZE - 1, 0);
std::cout << "get " << ret << " bytes os client data " << users[sockfd].buf << " from " << sockfd << std::endl;
heap_timer *timer = users[sockfd].timer;
if(ret < 0) //删除定时器 关闭描述符
{
if(errno != EAGAIN)
{
cb_func(&users[sockfd]);
if(timer)
th.del_timer(timer);
}
}
else if(ret == 0)
{
cb_func(&users[sockfd]);
if(timer)
th.del_timer(timer);
}
else
{
//调整定时器 延迟关闭
if(timer)
{
time_t cur = time(NULL);
timer->expire = time(NULL) + 3 * 5; //时间叠加
std::cout << "adjust timer once" << std::endl;
}
}
}
else
{
;
}
}
if(timeout)
{
timer_handler();
timeout = false;
}
}
close(listenfd);
close(pipefd[1]);
close(pipefd[0]);
delete []users;
return 0;
}
客户端可使用telnet测试。