epoll,reactor模型理解

网络编程,根本上来说是对网络IO技巧。在对多客户端的网络编程的一个很拥有的方式是通过epoll来管理socfd,通过epoll_create创建epoll对象,通过epoll_ctl加入想要关心sockfd,可以在sockfd相应事件触发时调度sockfd去处理网络数据。通过recv或send发送数据。

ractor模型,巧妙的利用epoll_event.data.ptr指针,引入较为复杂的数据结构,可以再触发相应事件的时候,通过回调对事件进行处理

struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = si;

下面是用于reactor模型的简易数据结构

struct sockitem{
    int sockfd;
    int (*callback)(int fd,int events,void*arg);

    char recvbuff[BUFFER_LEBGTH];
    char sendbuff[BUFFER_LEBGTH];

    int rlength;
    int slength;

    int status;
};

在上面的数据结构中,通过绑定sockfd与相应的回调处理函数,同时封装recvbuff,sendbuff。从而更方便的处理网络数据,下面是服务端初始化一个reactor简易服务端

    if(argc < 2){
        return -1;
    }


    int port = atoi(argv[1]);

    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0){
        return -1;
    }

    struct sockaddr_in addr;
    memset(&addr,0,sizeof(addr));


    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0){
        return -2;
    }

    if(listen(sockfd,5) < 0){
        return -3;
    }

    eventloop = (struct reactor*)malloc(sizeof(struct reactor));
    eventloop->epfd = epoll_create(1);

   
    struct sockitem *si = (struct sockitem*)malloc(sizeof(struct sockitem));
    si->sockfd = sockfd;
    si->callback = accept_cb;
    si->status = WS_INIT;

    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.ptr = si;
    epoll_ctl(eventloop->epfd,EPOLL_CTL_ADD,sockfd,&ev);

这样,accept与recv同属EPOLLIN事件,其各自事件触发后,可以方便的调用其回调函数,对网络数据进行处理,主处理代码如下

while(1){
        int nready = epoll_wait(eventloop->epfd,eventloop->events,1024,-1);

        if(nready < -1){
            break;
        }

        int i = 0;
        for(i = 0; i < nready;i++){
            if(eventloop->events[i].events & EPOLLIN ){
            struct sockitem *item =  (struct sockitem*)eventloop->events[i].data.ptr;

            item->callback(item->sockfd,events[i].events,item);
            }

            if(eventloop->events[i].events & EPOLLOUT){
                struct sockitem *item =  (struct sockitem*)events[i].data.ptr;

            item->callback(item->sockfd,events[i].events,item);
            }
        }
    }

这样,在主函数中,只要关注监听后处理accept事件,在accept回调中加入对客户端sockfd的关注,如下代码

int accept_cb(int fd,int events,void *arg){
    struct sockaddr_in clientaddr;
    memset(&clientaddr,0,sizeof(clientaddr));
    socklen_t client_len = sizeof(clientaddr);
    int clientfd = accept(fd,(struct sockaddr*)&clientaddr,&client_len);
    if(clientfd <= 0){
        return -1;
    }

    char str[INET_ADDRSTRLEN] = {0};
    printf("recv from %s at port %d\n",inet_ntop(AF_INET,&clientaddr.sin_addr,str,sizeof(str)),
    ntohs(clientaddr.sin_port));
    //ET 边缘触发 LT 有数据则一直触发  小块数据ET 循环读  LT大块

    struct sockitem *si = (struct sockitem*)malloc(sizeof(struct sockitem));
    si->sockfd = clientfd;
    si->callback = recv_cb;
    si->status = WS_HANDSHARK;

    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.ptr = si;
    epoll_ctl(eventloop->epfd,EPOLL_CTL_ADD,clientfd,&ev);
}

在accept的回调中关注EPOLLIN事件,当服务端收到客户端数据时,触发recv_cb,如下代码

int recv_cb(int fd,int events,void* arg){

    struct sockitem *si = (struct sockitem*)arg;
    int ret = recv(fd,si->recvbuff,1024,0);
    struct epoll_event ev;
    if(ret < 0){
        if(errno == EAGAIN || errno == EWOULDBLOCK){
            //
            return -1;
        } else{
            //断开
            close(fd);
            ev.events = EPOLLIN;
            ev.data.fd = fd;
            epoll_ctl(eventloop->epfd,EPOLL_CTL_DEL,fd,&ev);
            free(si);

        }
    }else if(ret == 0){
        //断开
        close(fd);
        ev.events = EPOLLIN;
        ev.data.fd = fd;
        epoll_ctl(eventloop->epfd,EPOLL_CTL_DEL,fd,&ev);
        free(si);
    }
    else{
        buffer[ret] = '\0';
        printf("recv %s\n",buffer);

        /*memcpy(si->sendbuff,si->recvbuff,si->rlength);

        si->slength = si->rlenth;*/


        if(si->status  == WS_HANDSHARK){
            handshark(si,eventloop);
        }else if(si->status == WS_DATATRANSFORM){

        }else if(si->status == WS_DATATRANSFORM){

        }else{

        }




        ev.events = EPOLLOUT | EPOLLET;

        si->sockfd = fd;
        si->callback = send_cb;
        ev.data.ptr = si;

        epoll_wait(eventloop->epfd,EPOLL_CTL_MOD,fd,&ev);
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值