时间堆:将所有定时器中超时最小的一个作为心搏间隔,这样,一旦心搏函数tick被调用,超时时间最小的一个定时器必然到期,我们就可以在tick函数里处理该定时器,这样就实现了较为精确的定时.
最小堆很适合这种方案,最小堆的堆顶永远为最小的元素,这样每次只要将堆顶的定时器处理,并将下一个堆顶元素的超时时间作为下一次心搏间隔即可实现上述方案
#ifndef _MIN_HEAP_H
#define _MIN_HEAP_H
#include<iostream>
#include<netinet/in.h>
#include<time.h>
using std::exception;
#define BUFFER_SIZE 64
class heap_timer;
class client_data
{
public:
sockaddr_in address;
int sockfd;
char buf[BUFFER_SIZE];
heap_timer *timer;
};
class heap_timer
{
public:
heap_timer(int delay)
{
expire = time(NULL)+delay;
}
time_t expire;
void (*cb_func)(client_data*);
client_data *user_data;
};
class time_heap
{
public:
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] = NULL;
}
}
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] = NULL;
}
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()
{
for(int i = 0;i<cur_size;++i)
{
delete array[i];
}
delete []array;
}
void add_timer(heap_timer *timer) throw(std::exception)
{
if(!timer)
{
return;
}
if(cur_size >= capacity)
{
resize();
}
int hole = cur_size++;
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;
}
void del_timer(heap_timer* timer)
{
if(!timer)
{
return;
}
timer->cb_func = NULL;//延迟销毁
}
heap_timer* top()const
{
if(empty())
{
return NULL;
}
return array[0];
}
void pop_timer()
{
if(empty())
{
return;
}
if(array[0])
{
delete array[0];
array[0] = array[--cur_size];
percolate_down(0);
}
}
void 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];
}
}
bool empty()const
{
return cur_size == 0;
}
private:
void percolate_down(int hole)
{
heap_timer* temp = array[hole];
int child = 0;
for( ;((hole*2+1) <= (cur_size-1));hole = child)
{
child = (hole*2+1);
if( (child < (cur_size-1) ) && (array[child+1]->expire < array[child]->expire) )
{
++child;
}
if( array[child]->expire < temp->expire)
{
array[hole] = array[child];
}
else
{
break;
}
}
array[hole] = temp;
}
void resize()throw(std::exception)
{
heap_timer** temp = new heap_timer*[2*capacity];
for(int i=0;i<2*capacity;++i)
{
temp[i] = NULL;
}
if(!temp)
{
throw std::exception();
}
capacity = 2*capacity;
for(int i=0;i<cur_size;++i)
{
temp[i] = array[i];
}
delete []array;
array = temp;
}
heap_timer** array;
int capacity;
int cur_size;
};
#endif
ser.cpp
#include"./min_heap.h"
#define FD_LIMIT 65535
#define MAX_EVENT_NUMBER 1024
#define TIMESLOT 10
using namespace std;
static int pipefd[2];
static time_heap th(10);
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)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
setnonblocking(fd);
}
void sig_handler(int sig)
{
int save_errno = errno;
int msg = sig;
send(pipefd[1],(char*)&msg,1,0);
errno = save_errno;
}
void time_handler()
{
th.tick();
if(!th.empty())
alarm((th.top()->expire-time(NULL)));//将定时器中最小超时时间(即堆顶计时器的超时时间)作为下一次心搏时间,这样可以减少tick函数被调用的次数,保证每次调用必然都有超时事件发生
else
alarm(TIMESLOT);
}
void addsig(int sig)
{
struct sigaction sa;
memset(&sa,'\0',sizeof(sa));
sa.sa_handler = sig_handler;
sa.sa_flags |=SA_RESTART;
sigfillset(&sa.sa_mask);
assert(sigaction(sig,&sa,NULL)!=-1);
}
void cb_func(client_data* user_data)
{
epoll_ctl(epollfd,EPOLL_CTL_DEL,user_data->sockfd,0);
assert(user_data);
char buf[38]= "long time no request,you are closed.";
send(user_data->sockfd,buf,strlen(buf),0);
close(user_data->sockfd);
printf("cllose fd %d\n",user_data->sockfd);
}
int main(int argc,char *argv[])
{
if(argc<=2)
{
printf("usage: %s ip port_number\n",basename(argv[0]));
return 1;
}
const char *ip = argv[1];
int port = atoi(argv[2]);
struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET,ip,&address.sin_addr);
int sock = socket(AF_INET,SOCK_STREAM,0);
assert(sock>=0);
int ret = bind(sock,(struct sockaddr*)&address,sizeof(address));
assert(ret!=-1);
ret = listen(sock,5);
assert(ret!=-1);
epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(5);
assert(epollfd!=-1);
addfd(epollfd,sock);
ret = socketpair(PF_UNIX,SOCK_STREAM,0,pipefd);
if(ret == -1)
{
printf("errno = %d\n",errno);
assert(ret!=-1);
}
setnonblocking(pipefd[1]);
addfd(epollfd,pipefd[0]);
addsig(SIGTERM);
addsig(SIGALRM);
int stop_sever = 0;
client_data* users = new client_data[MAX_EVENT_NUMBER];
int timeout = 0;
alarm(TIMESLOT);
while(!stop_sever)
{
int number = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
if(number<0)
{
if(errno == EINTR)
{
continue;
}
else
{
printf("errno = %d\n",errno);
printf("epoll_wait fail\n");
break;
}
}
int i=0;
for(;i<number;++i)
{
int sockfd = events[i].data.fd;
if(sockfd == sock)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int connfd = accept(sock,(struct sockaddr*)&client,&len);
addfd(epollfd,connfd);
users[connfd].address = client;
users[connfd].sockfd = connfd;
heap_timer* timer = new heap_timer(10);//**
timer->user_data = &users[connfd];
timer->cb_func = cb_func;
time_t cur = time(NULL);
timer->expire = cur +3*TIMESLOT;
users[connfd].timer = timer;
th.add_timer(timer);
}
else if((sockfd == pipefd[0])&&(events[i].events & EPOLLIN))
{
int sig;
char signals[1024];
ret = recv(pipefd[0],signals,sizeof(signals),0);
if(ret == -1)
{
//handle errno
continue;
}
else if(ret == 0)
{
continue;
}
else
{
int i=0;
for(;i<ret;++i)
{
switch(signals[i])
{
case SIGALRM:
{
timeout = 1;
break;
}
case SIGTERM:
{
stop_sever = 1;
}
}
}
}
}
else if(events[i].events & EPOLLIN)
{
memset(users[sockfd].buf,'\0',BUFFER_SIZE);
ret = recv(sockfd,users[sockfd].buf,BUFFER_SIZE-1,0);
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
{
printf("get %d bytes of client data %s from %d\n",ret,users[sockfd].buf,sockfd);
if(timer)
{
time_t cur = time(NULL);
timer->expire = cur +3*TIMESLOT;
printf("adjust timer once\n");
}
}
}
else
{
printf("something else happened\n");
}
}
if(timeout)
{
time_handler();
timeout = 0;
}
}
close(sock);
close(pipefd[0]);
close(pipefd[1]);
delete []users;//???
return 0;
}
cli.c:
#include"./utili1.h"
int main(int argc,char *argv[])
{
const char *ip = argv[1];
int port = atoi(argv[2]);
struct sockaddr_in addrSer;
bzero(&addrSer,sizeof(addrSer));
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(port);
inet_pton(AF_INET,ip ,&addrSer.sin_addr);
int sock = socket(AF_INET,SOCK_STREAM,0);
assert(sock>=0);
int ret = connect(sock,(struct sockaddr*)&addrSer,sizeof(addrSer));
assert(ret>=0);
char buf[50];
while(1)
{
scanf("%s",buf);
ret = send(sock,buf,strlen(buf),0);
if(ret<0)
{
printf("errno = %d\n",errno);
break;
}
/* ret = recv(sock,buf,38,MSG_DONTWAIT );
if(ret!=0)
{
printf("%s",buf);
break;
}
*/
}
/*
while(1)
{
ret = recv(sock,buf,50,0);
if(ret<0)
break;
else if(ret == 0)
{
printf("sever close connextion\n");
break;
}
else
printf("get %d bytes : %s\n",(int)strlen(buf),buf);
}
*/
close(sock);
}
tianxintong@txt:~/sigalarm/time_heap_sigalarm$