zoukankan      html  css  js  c++  java
  • epoll

    关于select/poll与epoll

    select/poll虽然避免了无限循环轮循, 但是有一个问题,那就是特定事件发生时,我们还是需要再次轮循一遍查看到底时哪个或哪几个fd产生了可读或可写事件
    epoll的等待事件返回后, 虽然返回值也是可用事件总数, 但可用事件会返回到一个指定的事件集合中,这个集合只包含已就绪事件,这么就节省了很多不必要的遍历时间

    相关函数与结构

    int epoll_create(int size); //size相当于select里的maxfd+1,linux v2.6.8内核后的版本已经忽略size参数
    int epoll_create1(int flags); //新的create函数, flags通常指定为EPOLL_CLOEXEC,表示进程调用exec函数后,关闭该描述符,而不像fork那样继承下来
     
    int epoll_ctl(int epfd,int op,stuct epoll_event *event);
    //epfd为epoll_create的返回值
    //op选项:EPOLL_CTL_ADD/EPOLL_CTL_MOD/EPOLL_CTL_DEL,用于添加或删除要监听的事件
     
    struct epoll_event{
        __uint32_t events;
        epoll_data_t data;
    };
    typedef union epoll_data{
        void *ptr;
        int fd;  //文件描述符fd
        __uint32_t u32;
        __uint32_t u64;
    }epoll_data_t;
    //events选项:EPOLLIN/EPOLLOUT/EPOLLET/EPOLLLT等
     
    int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);
    //epfd为epoll_create的返回值
    //events作为输出,为可读/可写事件的返回值,通常是一个结构数组
    //maxevents为上一个参数的对应数组的size
    //timeout 超时,毫秒
    //返回值是已经准备就绪的fd总数
     
     
    //man手册例子
               #define MAX_EVENTS 10
               struct epoll_event ev, events[MAX_EVENTS];
               int listen_sock, conn_sock, nfds, epollfd;
     
               /* Set up listening socket, 'listen_sock' (socket(),
                  bind(), listen()) */
     
               epollfd = epoll_create(10);
               if (epollfd == -1) {
                   perror("epoll_create");
                   exit(EXIT_FAILURE);
               }
     
               ev.events = EPOLLIN;
               ev.data.fd = listen_sock;
               if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
                   perror("epoll_ctl: listen_sock");
                   exit(EXIT_FAILURE);
               }
     
               for (;;) {
                   nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
                   if (nfds == -1) {
                       perror("epoll_pwait");
                       exit(EXIT_FAILURE);
                   }
     
                   for (n = 0; n < nfds; ++n) {
                       if (events[n].data.fd == listen_sock) {
                           conn_sock = accept(listen_sock,
                                           (struct sockaddr *) &local, &addrlen);
                           if (conn_sock == -1) {
                               perror("accept");
                               exit(EXIT_FAILURE);
                           }
                           setnonblocking(conn_sock);
                           ev.events = EPOLLIN | EPOLLET;
                           ev.data.fd = conn_sock;
                           if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                                       &ev) == -1) {
                               perror("epoll_ctl: conn_sock");
                               exit(EXIT_FAILURE);
                           }
                       } else {
                           do_use_fd(events[n].data.fd);
                       }
                   }
               }
    

    关于EPOLLET和EPOLLLT:
    ET模式仅当状态发生变化的时候才获得通知,这里所谓的状态的变化并不包括缓冲区中还有未处理的数据,也就是说,如果要采用ET模式,需要一直read/write直到出错为止,很多人反映为什么采用ET模式只接收了一部分数据就再也得不到通知了,大多因为这样;而LT模式是只要有数据没有处理就会一直通知下去的.

    epoll版server

    #include <unistd.h>
    #include <fcntl.h>
    #include <netdb.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <signal.h>
    #include <sys/wait.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <sys/epoll.h>
     
    #define MAX(a,b) a>b?a:b;
     
    void err_quit(const char *s){
        perror(s);
        exit(1);
    }
     
    ssize_t readn(int fd,void *buff,size_t count){
        char *buffp;
        ssize_t nread;
        size_t nleft;
     
        buffp=(char *)buff;
        nleft=count;
        while(nleft > 0){
            if((nread = read(fd,buffp,nleft)) < 0){
                if(errno == EINTR)
                    continue;
                else
                    return -1;
            }else if(nread == 0)
                break;
            nleft -= nread;
            buffp += nread;
        }
        return count-nleft;
    }
     
    ssize_t writen(int fd,const void *buff,size_t n){
        size_t nleft;
        ssize_t nwritten;
        const char *ptr;
     
        ptr=buff;
        nleft=n;
        while(nleft > 0){
            if((nwritten=write(fd,ptr,nleft)) < 0){
                if(nwritten < 0 && errno == EINTR)
                    continue;
                else
                    return -1;
            }else if(nwritten == 0)
                break;
            nleft -= nwritten;
            ptr += nwritten;
        }
        return n-nleft;
    }
     
    ssize_t recv_peek(int fd,void *buf,size_t len){
        ssize_t ret;
        while(1){
            ret=recv(fd,buf,len,MSG_PEEK);
            if(ret == -1 && errno == EINTR)
                continue;
            return ret;
        }
    }
     
    ssize_t readline(int fd,void *buf,size_t maxline){
        ssize_t ret;
        size_t nread;
        size_t nleft;
        char *bufp;
     
        bufp=buf;
        nleft=maxline;
        while(1){
            ret=recv_peek(fd,buf,nleft);
            if(ret < 0)
                return ret;
            else if(ret == 0)
                return ret;
     
            nread=ret;
            int i;
            for(i=0;i<nread;i++){
                if(bufp[i] == '
    '){
                    ret=readn(fd,bufp,i+1);
                    if(ret != i+1)
                        err_quit("readn");
     
                    return ret;
                }
            }
     
            if(nread > nleft)
                err_quit("readn");
     
            nleft -= nread;
            ret=readn(fd,bufp,nread);
            if(ret != nread)
                err_quit("readn");
     
            bufp += nread;
        }
     
        return -1;
    }
     
    int accept_timeout(int fd,struct sockaddr_in *addr,unsigned int wait_seconds){
        int ret;
        socklen_t addrlen=sizeof(struct sockaddr_in);
     
        if(wait_seconds > 0){
            fd_set accept_fdset;
            struct timeval timeout;
     
            FD_ZERO(&accept_fdset);
            FD_SET(fd,&accept_fdset);
     
            timeout.tv_usec=0;
            timeout.tv_sec=wait_seconds;
            do{
                ret=select(fd+1,&accept_fdset,NULL,NULL,&timeout);
            }while(ret < 0 && errno == EINTR);
     
            if(ret == 0){
                errno = ETIMEDOUT;
                return -1;
            }else if(ret == -1)
                return -1;
        }
     
        if(addr != NULL)
            ret=accept(fd,(struct sockaddr *)addr,&addrlen);
        else
            ret=accept(fd,NULL,NULL);
        if(ret == -1)
            err_quit("accept");
     
        return ret;
    }
    void activate_nonblock(int fd){
        int ret;
        int flags=fcntl(fd,F_GETFL);
        if(flags == -1)
            err_quit("fcntl");
     
        flags |= O_NONBLOCK;
        ret=fcntl(fd,F_SETFL,flags);
        if(ret == -1)
            err_quit("fcntl");
    }
     
    void deactivate_nonblock(int fd){
        int ret;
        int flags=fcntl(fd,F_GETFL);
        if(flags == -1)
            err_quit("fcntl");
     
        flags &= ~O_NONBLOCK;
        ret=fcntl(fd,F_SETFL,flags);
        if(ret == -1)
            err_quit("fcntl");
    }
     
    int main(int argc,char *argv[]){
        int i,tmpfd,sockfd,connfd,epollfd;
        socklen_t len;
        struct sockaddr_in addr,client;
        int nready;
        struct epoll_event ev,events[10];
        ssize_t n;
        char buf[1024];
     
     
        if((sockfd=socket(PF_INET,SOCK_STREAM,0)) < 0)
            err_quit("sockfd");
     
        bzero(&addr,sizeof(addr));
        addr.sin_family=AF_INET;
        addr.sin_addr.s_addr=htonl(INADDR_ANY);
        addr.sin_port=htons(5566);
     
        int on=1;
        if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) <0)
            err_quit("setsockopt");
     
        if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr))<0)
            err_quit("bind");
     
        if(listen(sockfd,10)<0)
            err_quit("listen");
     
        epollfd=epoll_create(10);
        if(epollfd == -1)
            err_quit("epoll_create");
     
        ev.events=EPOLLIN;
        ev.data.fd=sockfd;
        if(epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&ev) < 0)
            err_quit("epoll_ctl_add");
        while(1){
            nready=epoll_wait(epollfd,events,10,-1);
     
            if(nready == -1){
                if(errno == EINTR)
                    continue;
                else
                    err_quit("select");
            }
     
            for(i=0;i<nready;i++){
                if(events[i].data.fd == sockfd){
                    len=sizeof(client);
                    connfd=accept(sockfd,(struct sockaddr *)&client,&len);
                    if(connfd < 0)
                        err_quit("accept");
     
                    activate_nonblock(connfd);
                    ev.events=EPOLLIN | EPOLLET;
                    ev.data.fd=connfd;
                    if(epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&ev) < 0)
                        err_quit("epoll_ctl_add");
                }else if(events[i].data.fd & EPOLLIN){
                    tmpfd=events[i].data.fd;
                    bzero(buf,sizeof(buf));
                    if((n=readline(tmpfd,buf,sizeof(buf))) == 0){
                        if(epoll_ctl(epollfd,EPOLL_CTL_DEL,tmpfd,&ev) <0)
                            err_quit("epoll_ctl_del");
                        close(tmpfd);
                    }
                    write(STDOUT_FILENO,buf,n);
                    writen(tmpfd,buf,n);
                }else if(events[i].data.fd & EPOLLOUT){
                    //do something
                }
            }
        }
    }
    
  • 相关阅读:
    结对项目电梯调度--设计模拟
    程序的单元测试
    一个文本单词统计的案例
    MFC vs2012 Office2013 读写excel文件
    Unix NetWork Programming(unix环境编程)——环境搭建(解决unp.h等源码编译问题)
    VMware三种上网模型
    矩阵求逆算法及程序实现(C++)
    unix环境高级编程基础知识之第四章
    2014阿里研发面试题目
    MFC下debug改成release版本出现问题及解决办法
  • 原文地址:https://www.cnblogs.com/cfans1993/p/6131891.html
Copyright © 2011-2022 走看看