epoll反应堆模型

12 篇文章 9 订阅
1 篇文章 0 订阅

参考 <https://blog.csdn.net/daaikuaichuan/article/details/83862311>

参考 <https://www.bilibili.com/video/BV1iJ411S7UA>

求职期间,还是得好好学习。

看了大概的思路,自己理解着敲一下。和原来代码的有些地方不同。

视频有提到,为什么反应堆模式要注册写事件,因为可能写缓存满了(对方接收窗口小了,接收不过来)。

#include <stdio.h>
#include <string.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/epoll.h>

#include <errno.h>

#include <time.h>

#include <fcntl.h>

#define MAX_EVENTS 1024 /*监听上限*/
#define BUFLEN 4096     /*缓存区大小*/
#define SERV_PORT 6666  /*端口号*/

/*描述就绪文件描述符的相关信息*/
struct myevent_s{
    int fd;                                      //要监听的文件描述符
    int events;                                  //对应的监听事件,EPOLLIN和EPLLOUT
    void *arg;                                   //指向自己结构体指针
    void (*callbak)(int fd,int events,void* arg);//回调函数
    int status;                                  //是否在监听:1->在红黑树上(监听), 0->不在(不监听)
    char buf[BUFLEN];
    int len;
    long last_active;                            //记录每次加入红黑树 g_efd 的时间值
};


int g_efd;                                        //全局变量,作为红黑树根
struct myevent_s g_events[MAX_EVENTS+1];          //自定义结构体类型数组. +1-->listen fd


void eventinit(struct myevent_s *ev,int fd,void *arg){
    ev->fd = fd;
    ev->events = 0;
    ev->arg = arg;
    ev->callbak = NULL;
    ev->status = 0;
    memset(ev->buf,0,BUFLEN);
    ev->len = 0;
    ev->last_active = time(NULL);
}

void eventadd(int efd,struct myevent_s *ev,int events,void (*callbak)(int fd,int events,void* arg)){
    if(ev->status == 1){
        return;
    }

    struct epoll_event epv;
    int op;

    ev->events = events;
    ev->callbak = callbak;
    ev->last_active = time(NULL);

    epv.data.ptr = (void*)ev;
    epv.events = events;
    op = EPOLL_CTL_ADD;
    ev->status = 1;

    epoll_ctl(efd,op,ev->fd,&epv);

    return;
}

void eventdel(int efd,struct myevent_s *ev){
    ev->status = 0;
    epoll_ctl(efd,EPOLL_CTL_DEL,ev->fd,NULL);
}

void senddata(int fd, int events, void *arg);
void recvdata(int fd, int events, void *arg);

void recvdata(int fd, int events, void *arg){
    struct myevent_s *ev = (struct myevent_s *)arg;

    ev->len = recv(fd,ev->buf,sizeof(ev->buf),0);
    printf("fd[%d] recv [%d]%s\n",fd,ev->len,ev->buf);
    eventdel(g_efd,ev);

    if(ev->len > 0){
        eventadd(g_efd,ev,EPOLLOUT|EPOLLET,senddata);
    }
    else if(ev->len == 0){
        close(fd);
        ev->fd = -1;
    }else{
        if(errno == EINTR){
            eventadd(g_efd,ev,EPOLLIN|EPOLLET,recvdata);
        }else{
            printf("%d %s\n",errno,strerror(errno));
            close(fd);
            ev->fd = -1;
        }
    }
    return;
}

void senddata(int fd, int events, void *arg){
    struct myevent_s *ev = (struct myevent_s *)arg;

    int len = send(fd,ev->buf,ev->len,0);
    printf("fd[%d] send [%d]%s\n",fd,len,ev->buf);
    eventdel(g_efd,ev);

    if(len > 0){
        eventadd(g_efd,ev,EPOLLIN|EPOLLET,recvdata);
    }
    else if(len == 0){
        close(fd);
        ev->fd = -1;
    }else{
        if(errno == EINTR){
            eventadd(g_efd,ev,EPOLLOUT|EPOLLET,senddata);
        }else{
            printf("%d %s\n",errno,strerror(errno));
            close(fd);
            ev->fd = -1;
        }
    }
    return;
}


void acceptconn(int fd,int events,void *arg){
    int client_fd;
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    client_fd = accept(fd,(struct sockaddr*)&client_addr,&client_addr_len);
    if(client_fd < 0){
        printf("%d %s\n",errno,strerror(errno));
        return;
    }

    char buf[20];
    printf("accpet %s:%d\n",
        inet_ntop(AF_INET,&client_addr.sin_addr,buf,sizeof(buf)),
        ntohs(client_addr.sin_port));
    
    int i;
    for(i=0;i<MAX_EVENTS;i++){
        if(g_events[i].fd == -1){
            break;
        }
    }
    if(i == MAX_EVENTS){
        printf("full\n");
        close(client_fd);
        return;
    }

    int flag = fcntl(client_fd,F_GETFL);
    fcntl(client_fd,F_SETFL,flag|O_NONBLOCK);

    struct myevent_s *ev = &g_events[i];
    eventinit(ev,client_fd,(void*)ev);
    eventadd(g_efd,ev,EPOLLIN|EPOLLET,recvdata);

    return;
}

void initlistensocket(int efd,short port){
    struct sockaddr_in addr;
    int listen_fd;

    listen_fd = socket(AF_INET,SOCK_STREAM,0);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    bind(listen_fd,(struct sockaddr*)&addr,sizeof(addr));

    listen(listen_fd,5);

    struct myevent_s *ev = &g_events[MAX_EVENTS];
    eventinit(ev,listen_fd,(void*)ev);
    eventadd(g_efd,ev,EPOLLIN,acceptconn);

    return;
}

int main(){
    int i;
    for(i=0;i<MAX_EVENTS+1;i++){
        g_events[i].fd = -1;
    }

    g_efd = epoll_create(MAX_EVENTS+1);

    initlistensocket(g_efd,SERV_PORT);

    struct epoll_event events[MAX_EVENTS + 1];
    int nfd;
    struct myevent_s *ev;

    printf("server running:port[%d]\n", SERV_PORT);
    int checkpos = 0;
    long now,duration;

    for(;;){
        nfd = epoll_wait(g_efd, events, MAX_EVENTS+1, 1000);
        if(nfd < 0){
            printf("%d %s\n",errno,strerror(errno));
            if(errno == EINTR){
                continue;
            }else{
                break;
            }
        }

        if(nfd == 0){//超时
            now = time(NULL);
            for(i=0;i<100;i++,checkpos++){
                if(checkpos == MAX_EVENTS);
                    checkpos = 0;
                if(g_events[checkpos].fd ==-1 || g_events[checkpos].status != 1)
                    continue;
                duration = now -g_events[checkpos].last_active;
                if(duration >= 60)
                {
                    printf("[fd=%d] timeout\n", g_events[checkpos].fd);
                    eventdel(g_efd, &g_events[checkpos]);
                    close(g_events[checkpos].fd);
                    g_events[checkpos].fd = -1;
                }
            }

        }

        for(i=0;i<nfd;i++){
            ev = (struct myevent_s *)events[i].data.ptr;

            //如果监听的是读事件,并返回的是读事件
            if((events[i].events & EPOLLIN) &&(ev->events & EPOLLIN))
            {
                ev->callbak(ev->fd, events[i].events, ev->arg);
            }

            //如果监听的是写事件,并返回的是写事件
            else if((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT))
            {
                ev->callbak(ev->fd, events[i].events, ev->arg);
            }
        }
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值