服务器程序通常要定期处理非活动连接:给客户端发送一个重连请求,或者关闭该连接。利用alarm函数周期性的触发SIGALRM信号, 该信号的信号处理函数利用管道通知主循环执行定时器链表上的定时器任务---关闭非活动的连接。
采用升序双向链表实现定时器的功能,核心函数tick,它每隔一端固定时间就执行一次,以检测并处理到期的任务。判断定时器任务到期的依据是定时器的expire值小于当前的系统时间。从执行的效率来看,添加定时器的时间复杂度是O(n), 删除定时器的时间复杂度是O(1),执行定时器任务的时间复杂度是O(1).
#ifndef _LIST_TIMER
#define _LIST_TIMER
//基于升序定时器链表
#include
#include
#define BUFFER_SIZE 64
class util_timer; /*前向声明*/
/***************************
客户端socket地址
socket文件描述符
读缓存
定时器
***************************/
struct client_data{
struct sockaddr_in address;
int sockfd;
char buf[BUFFER_SIZE];
util_timer *timer;
};
/******定时器类*******/
class util_timer{
public:
util_timer(){}
void (*cb_func)(client_data *); //任务回调函数
public:
time_t expire; //任务的超时时间
client_data *user_data{nullptr};
util_timer *prev{nullptr}; //指向前一个定时器
util_timer *next{nullptr}; //指向下一个定时器
};
/*************定时器链表*******************/
/*升序 双向链表*/
class sort_timer_list{
public:
sort_timer_list(){}
~sort_timer_list(); //删除其中所有的定时器
void add_timer(util_timer *timer); //将目标定时器添加到链表
void adjust_timer(util_timer *timer); //调整定时器在链表中的位置(定时任务延长)
void del_timer(util_timer *timer); //从链表中删除
void tick(); //SIGALRM信号每次被触发,就在信号处理函数中执行一次tick
private:
//重载函数 该函数将目标定时器timer添加到list_head之后的部分链表中
void add_timer(util_timer *timer, util_timer *list_head);
private:
util_timer *head{nullptr};
util_timer *tail{nullptr};
};
//释放所有定时器结点
sort_timer_list::~sort_timer_list()
{
util_timer *tmp = head;
while(tmp)
{
head = tmp->next;
delete tmp;
tmp = head;
}
}
//目标定时器添加到链表
void sort_timer_list::add_timer(util_timer *timer)
{
if(!timer)
return;
if(!head) //链表为空
{
head = tail = timer;
return;
}
if(timer->expire < head->expire) //如果新插入的定时任务小于链表头部定时器 将该节点插入头部 修改头指针
{
timer->next = head;
head->prev = timer;
head = timer;
return;
}
add_timer(timer, head); //不满足上述条件 就需要遍历链表找到合适位置插入 //该函数为重载
return ;
}
//调整定时器在链表中的位置
void sort_timer_list::adjust_timer(util_timer *timer)
{
if(!timer)
return ;
util_timer *tmp = timer->next;
if(!tmp || timer->expire < tmp->expire) //如果要调整的结点为尾结点 或比先一个结点小 无需调整
return;
if(timer == head) //如果需要调整的结点为链表头结点
{
head = head->next;
head->prev = nullptr;
timer->next = nullptr;
add_timer(timer, head);
}
else
{
timer->prev->next = timer->next;
timer->next->prev = timer->prev;
timer->prev = nullptr;
timer->next = nullptr;
add_timer(timer, head);
}
return ;
}
void sort_timer_list::del_timer(util_timer *timer)
{
if(!timer)
return;
if(timer == head && timer == tail) //链表只有一个结点 且为要删除的结点
{
delete timer;
head = nullptr;
tail = nullptr;
return;
}
if(timer == head) //要删除的结点为头结点
{
head = head->next;
head->prev = nullptr;
delete timer;
return;
}
if(timer == tail) //要删除的为尾结点
{
tail = tail->prev;
tail->next = nullptr;
delete timer;
return;
}
//否则
timer->prev->next = timer->next;
timer->next->prev = timer->prev;
delete timer;
return;
}
//SIGALRM 处理到期的定时器
void sort_timer_list::tick()
{
if(!head)
return;
std::cout << "timer tick" << std::endl;
time_t cur = time(NULL); //获取系统当前时间
util_timer *tmp = head;
//从头结点开始处理每个定时器 知道遇到尚未到期的定时器
while(tmp)
{
if(cur < tmp->expire) //之后未到期
break;
tmp->cb_func(tmp->user_data); //执行定时任务
head = tmp->next;
if(head)
head->prev = nullptr;
delete tmp;
tmp = head;
}
return ;
}
//重载函数
void sort_timer_list::add_timer(util_timer *timer, util_timer *list_timer)
{
util_timer *prev = list_timer;
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)
{
prev->next = timer;
timer->prev = prev;
timer->next = nullptr;
tail = timer;
}
return;
}
#endif
服务器测试程序
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "List_Timer.hpp"
#define FD_LIMIT 65535
#define MAX_EVENT_NUMBER 1024
#define TIMESLOT 5
static int pipefd[2];
static sort_timer_list timer_list;
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()
{
timer_list.tick();
alarm(TIMESLOT);
}
//回调函数
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);
//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;
//创建定时器
util_timer *timer = new util_timer();
timer->user_data = &users[connfd];
timer->cb_func = cb_func; //回调函数
time_t cur = time(NULL);
timer->expire = cur + 3 * TIMESLOT;
users[connfd].timer = timer;
timer_list.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;
util_timer *timer = users[sockfd].timer;
if(ret < 0) //删除定时器 关闭描述符
{
if(errno != EAGAIN)
{
cb_func(&users[sockfd]);
if(timer)
timer_list.del_timer(timer);
}
}
else if(ret == 0)
{
cb_func(&users[sockfd]);
if(timer)
timer_list.del_timer(timer);
}
else
{
//调整定时器 延迟关闭
if(timer)
{
time_t cur = time(NULL);
timer->expire = cur + 3 * TIMESLOT;
std::cout << "adjust timer once" << std::endl;
timer_list.adjust_timer(timer);
}
}
}
else
{
;
}
}
if(timeout)
{
timer_handler();
timeout = false;
}
}
close(listenfd);
close(pipefd[1]);
close(pipefd[0]);
delete []users;
return 0;
}
客户端可使用telnet测试。