时间堆实现定时器


时间堆:将所有定时器中超时最小的一个作为心搏间隔,这样,一旦心搏函数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"./utili.h"
#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$


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值