项目中定时器应用2

定时器处理非活动连接

在这我们主要使用的是前面介绍的第二种信号量的方法。
如果某一用户connect()到服务器之后,长时间不交换数据,一直占用服务器端的文件描述符,导致连接资源的浪费。这时候就应该利用定时器把这些超时的非活动连接释放掉,关闭其占用的文件描述符。这种情况也很常见,当你登录一个网站后长时间没有操作该网站的网页,再次访问的时候你会发现需要重新登录。

项目中使用的是SIGALRM信号来实现定时器,利用alarm函数周期性的触发SIGALRM信号,信号处理函数利用管道通知主循环,主循环接收到该信号后对升序链表上所有定时器进行处理,若该段时间内没有交换数据,则将该连接关闭,释放所占用的资源。

上一篇文章用代码实现了升序定时器链表,在这我们利用这个升序定时器链表处理这个非活动连接。

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/uio.h>
#include<string.h>
#include "lst_time.h"

#define FD_LIMIT 65535
#define  MAX_EVENT_NUMBER 1024
#define TIMESLOT 5

static int pipefd[2];
static sort_timer_lst timer_lst;
static int epollfd=0;
int setnonblocking(int fd)
{
    int old_option = fcntl(fd,F_GETFL);//这个函数主要提供对文件描述符的各种控制操作,fd是被操作的文件描述符,后面这个参数是获取fd的状态标志,返回值就是fd的状态标志
    int new_option=old_option|O_NONBLOCK;
    fcntl(fd,F_SETFL,new_option);//设置fd的状态标志
    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);//send是往文件描述符pipefd[1]写入数据,接下来两个参数分别是缓冲区位置和大小,TCP数据读写
    errno = save_errno;
}
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 timer_handler()
{
    timer_lst.tick();//定时处理任务
    alarm(TIMESLOT);//因为一次alarm调用只会引起一次SIGALRM信号,所以需要重新定时,以不断触发SIGALRM信号
}
//定时器回调函数,它删除非活动连接socket上的注册事件,并关闭
void cb_func(client_data* user_data)
{
    epoll_ctl(epollfd,EPOLL_CTL_DEL,user_data->sockfd,0);
    assert(user_data);
    close(user_data->sockfd);
    printf("Close fd %d\n",user_data->sockfd);
}
int main(int argc,char* argv[] )
{
    if(argv<=2)
    {
        char* ip_address=argv[0];
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    int ret=0;
    struct sockaddr_in address;
    bzero(&address,sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET,IP,&address.sin_addr);
    address.sin_port=htons(port);

    int listenfd = socket(PF_INET,SOCK_STREAM,0);//使用默认协议
    assert(listenfd>=0);

    ret = bind(lestenfd,(struct sockaddr*)&address,sizeof(address));
    assert(ret!=-1);

    ret=listen(lestenfd,5);
    assert(ret!=-1);

    epoll_event events[MAX_EVENT_NUMBER];
    int eppllfd = 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 number = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
        if((number<0)&&(errno!=EINTR))
        {
            printf("epoll failure\n");
            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);

                addfd(epollfd,connfd);
                users[connfd].address = client_address;
                users[connfd].sockfd=connfd;
                //创建定时器,设置其回调函数与超时时间,然后绑定定时器与用户数据,最后将定时器添加到链表TIMER_LST中
                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_lst.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)
                {
                    continue;
                }
                else 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);
                printf("从文件描述符的哪个位置读了多少字节的数据");

                util_timer* timer = users[sockfd].timer;
                if(ret<0)
                {
                    //如果发生错误,则关闭连接,并移除对应的定时器
                    if(errno!=EAGAIN)
                    {
                        cb_func(&users[sockfd]);
                        if(timer)
                        {
                            timer_lst.del_timer(timer);
                        }
                    }
                }
                else if(ret==0)
                {
                    //如果对方已经关闭连接,则我们也关闭连接,并移除对应的定时器
                    cb_func(&users[sockfd]);
                    if(timer)
                    {
                        timer_lst.del_timer(timer);
                    }
                }
                else
                {
                    //如果某个客户连接上有数据可读,则我们要调整连接对应的定时器,以延迟该连接被关闭的时间
                    if(timer)
                    {
                        time_t cur = time(NULL);
                        timer->expire = cur+3*TIMESLOT;
                        printf("adjust timer once\n");
                        timer_lst.adjust_timer(timer);
                    }
                }
            }
        }
        if(timeout)
        {
            timer_handler();
            timeout = false;
        }
    }
    close(listenfd);
    close(pipefd[1]);
    close(pipefd[0]);
    delete[] users;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值