Linux之定时器
Linux下的实现方式
-
socket选项SO_RECVTIMEO和SO_SNDTIMEO
-
SIGALRM信号
信号相关笔记:Linux之信号 -
I/O复用系统调用的超时参数
定时器及其容器
-
定时器
封装了以下内容-
超时时间
-
回调函数
:就是每次时间到了要干嘛 -
连接资源
:包括客户端socket地址、客户端socket、定时器
-
-
定时器的容器
把多个定时器串联组织起来统一处理。比如用一个升序链表来装定时器,每个定时器是一个结点
具体代码
定时器类定义
//在连接资源中包含定时器,定时器中包含连接资源,他们均可以找到对方
class util_timer;
//连接资源
struct client_data
{
//客户端socket地址
sockaddr_in address;
//客户端socket
int sockfd;
//定时器
util_timer* timer;
};
//定时器类,即链表中的结点
class util_timer
{
public:
util_timer() : prev( NULL ), next( NULL ){}
public:
//超时时间
time_t expire;
//回调函数,从内核事件表删除事件,关闭文件描述符,释放连接资源
void (*cb_func)( client_data* );
//连接资源
client_data* user_data;
//上一个结点
util_timer* prev;
//下一个结点
util_timer* next;
};
使用定时器
//定时处理任务
void timer_handler()
{
timer_lst.tick(); //tick函数遍历链表,处理到时了的事件
alarm(TIMESLOT); //重新定时,不断触发SIGALRM信号
}
//定时器链表
static sort_timer_lst timer_lst;
//创建连接资源数组
client_data* users_timer = new client_data[MAX_FD];
//超时默认为False
bool timeout = false;
//alarm定时触发SIGALRM信号
alarm(TIMESLOT);
while (!stop_server)
{
int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
if (number < 0 && errno != EINTR)
{
break;
}
//处理就绪事件
for (int i = 0; i < number; i++)
{
int sockfd = events[i].data.fd;
//如果是新到的客户连接
if (sockfd == listenfd)
{
//初始化客户端连接地址
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
//该连接分配的文件描述符
int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
//初始化该连接对应的连接资源
users_timer[connfd].address = client_address;
users_timer[connfd].sockfd = connfd;
//正常创建链表结点
util_timer* timer = new util_timer;
//设置定时器对应的连接资源
timer->user_data = &users_timer[connfd];
//设置回调函数
timer->cb_func = cb_func;
time_t cur = time(NULL);
//设置绝对超时时间
timer->expire = cur + 3 * TIMESLOT;
//创建该连接对应的定时器
users_timer[connfd].timer = timer;
//将该定时器添加到链表中
timer_lst.add_timer(timer);
}
//处理异常事件
else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
{
//服务器端关闭连接,移除对应的定时器
cb_func(&users_timer[sockfd]);
util_timer* timer = users_timer[sockfd].timer;
if (timer)
{
timer_lst.del_timer(timer);
}
}
//处理定时器信号
else if ((sockfd == pipefd[0]) && (events[i].events & EPOLLIN))
{
//接收到SIGALRM信号,timeout设置为True
}
//处理客户连接上接收到的数据
else if (events[i].events & EPOLLIN)
{
//创建定时器临时变量,将该连接对应的定时器取出来
util_timer* timer = users_timer[sockfd].timer;
if (users[sockfd].read_once())
{
//若监测到读事件,将该事件放入请求队列
pool->append(users + sockfd);
//若有数据传输,则将定时器往后延迟3个单位
//对其在链表上的位置进行调整
if (timer)
{
time_t cur = time(NULL);
timer->expire = cur + 3 * TIMESLOT;
timer_lst.adjust_timer(timer);
}
}
else
{
//服务器端关闭连接,移除对应的定时器
cb_func(&users_timer[sockfd]);
if (timer)
{
timer_lst.del_timer(timer);
}
}
}
else if (events[i].events & EPOLLOUT)
{
util_timer* timer = users_timer[sockfd].timer;
if (users[sockfd].write())
{
//若有数据传输,则将定时器往后延迟3个单位
//并对新的定时器在链表上的位置进行调整
if (timer)
{
time_t cur = time(NULL);
timer->expire = cur + 3 * TIMESLOT;
timer_lst.adjust_timer(timer);
}
}
else
{
//服务器端关闭连接,移除对应的定时器
cb_func(&users_timer[sockfd]);
if (timer)
{
timer_lst.del_timer(timer);
}
}
}
}
//处理定时器为非必须事件,收到信号并不是立马处理
//完成读写事件后,再进行处理
if (timeout)
{
timer_handler();
timeout = false;
}