• epoll学习


    一、epoll_create

    #include <sys/epoll.h>
    
    int epoll_create(int size);
    int epoll_create1(int flags);
    返回:成功非负文件描述符,-1出错
    size:内核监听数目一共多大

    创建一个epoll接口,size参数和select不同,不是fd+1?

    需要注意的是:当创建好epoll后,它就会占用一个fd值,在linux /proc/id/fd/能看到这个fd的,所以使用完epoll后,必须close()关闭,否则可能导致耗尽fd。

    二、epoll_ctl

    #include <sys/epoll.h>
    
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    返回:0成功,-1失败
    epfd:由epoll_create生成的epoll专用的文件描述符
    op:要进行的操作,可能取EPOLL_CTL_ADD注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除
    fd:关联的文件描述符
    event:指向epoll_event指针

     epoll的相关的epoll_event结构:

    typedef union epoll_data {
        void *ptr;
        int fd;
        __uint32_t u32;
        __uint64_t u64;
    } epoll_data_t;
    
    struct epoll_event {
        __uint32_t events; /* Epoll events */
        epoll_data_t data; /* User data variable */
    };

    其中的events可以是以下几个宏的集合:

    EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
    EPOLLOUT:表示对应的文件描述符可以写;
    EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
    EPOLLERR:表示对应的文件描述符发生错误;
    EPOLLHUP:表示对应的文件描述符被挂断;
    EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
    EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

    三、epoll_wait

    #include <sys/epoll.h>
    
    int epoll_wait(int epfd, struct epoll_event *events,
            int maxevents, int timeout);
    
    int epoll_pwait(int epfd, struct epoll_event *events,
            int maxevents, int timeout, const sigset_t *sigmask);
    返回:发生事件数
    epfd:由epoll_create生成的epoll专用的文件描述符
    epoll_event:用于回传代处理事件的数组
    maxevents:每次能处理的事件数
    timeout:等待I/O事件发生的超时值

    在linux中的man手册中有epoll模型:

     1            #define MAX_EVENTS 10
     2            struct epoll_event ev, events[MAX_EVENTS];
     3            int listen_sock, conn_sock, nfds, epollfd;
     4 
     5            /* Set up listening socket, 'listen_sock' (socket(),
     6               bind(), listen()) */
     7 
     8            epollfd = epoll_create(10);
     9            if (epollfd == -1) {
    10                perror("epoll_create");
    11                exit(EXIT_FAILURE);
    12            }
    13 
    14            ev.events = EPOLLIN;
    15            ev.data.fd = listen_sock;
    16            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
    17                perror("epoll_ctl: listen_sock");
    18                exit(EXIT_FAILURE);
    19            }
    20 
    21            for (;;) {
    22                nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);  //等待IO事件
    23                if (nfds == -1) {
    24                    perror("epoll_pwait");
    25                    exit(EXIT_FAILURE);
    26                }
    27 
    28                for (n = 0; n < nfds; ++n) {
    29                //如果是主socket事件,则表示有新连接进入,需要进行新连接的处理
    30                    if (events[n].data.fd == listen_sock) {
    31                        conn_sock = accept(listen_sock,
    32                                        (struct sockaddr *) &local, &addrlen);
    33                        if (conn_sock == -1) {
    34                            perror("accept");
    35                            exit(EXIT_FAILURE);
    36                        }
    37                        //将新连接置于非阻塞模式
    38                        setnonblocking(conn_sock);
    39                        ev.events = EPOLLIN | EPOLLET;
    40                        //注意这里的参数EPOLLIN|EPOLLET并没有设置对写socket的监听
    41                        //如果有写操作的话,这个时候epoll是不会返回事件的
    42                        //如果要对写操作也监听的话,应该是EPOLLIN|EPOLLOUT|EPOLLET
    43                        //并且将新连接也加入EPOLL的监听队列
    44                        ev.data.fd = conn_sock;
    45                        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
    46                                    &ev) == -1) {
    47                            //加入到epoll的监听队列里,这里用EPOLL_CTL_ADD
    48                            //来加一个新的epoll事件,可以通过EPOLL_CTL_DEL减少
    49                            //一个epoll事件,通过EPOLL_CTL_MOD来改变一个i额事件的监听方式
    50                            perror("epoll_ctl: conn_sock");
    51                            exit(EXIT_FAILURE);
    52                        }
    53                    } else {
    54                        //如果不是主socket的事件的话,则代表这是一个用户的socket事件
    55                        //则用来处理这个用户的socket的事情是,比如说read(fd, xxx)之类,或者一些其他的处理
    56                        do_use_fd(events[n].data.fd);
    57                    }
    58                }
    59            }    
    epoll模型

     下面是个epoll的使用例子:

      1 #include <iostream>
      2 #include <sys/socket.h>
      3 #include <sys/epoll.h>
      4 #include <netinet/in.h>
      5 #include <arpa/inet.h>
      6 #include <fcntl.h>
      7 #include <unistd.h>
      8 #include <stdio.h>
      9 #include <errno.h>
     10 #include <stdlib.h>
     11 #include <strings.h>
     12 
     13 using namespace std;
     14 
     15 #define MAXLINE   5
     16 #define OPEN_MAX  100
     17 #define LISTENQ   20
     18 #define SERV_PORT 5000
     19 #define INFTIM    1000
     20 
     21 void setnonblocking(int sock)
     22 {
     23     int opts;
     24     opts = fcntl(sock, F_GETFL);
     25     if(opts < 0) {
     26         perror("fcntl(sock, GETFL)");
     27         exit(1);
     28     }
     29     opts = opts | O_NONBLOCK;
     30     if(fcntl(sock, F_SETFL, opts) < 0) {
     31         perror("fcntl(sock, SETFL, opts)");
     32         exit(1);
     33     }
     34 }
     35 
     36 int main()
     37 {
     38     int i, maxi, listenfd, connfd, sockfd, epfd, nfds;
     39     ssize_t n;
     40     char line[MAXLINE];
     41     socklen_t clilen;
     42 
     43     struct epoll_event ev, events[20];
     44     epfd = epoll_create(256);
     45     struct sockaddr_in clientaddr;
     46     struct sockaddr_in serveraddr;
     47     listenfd = socket(AF_INET, SOCK_STREAM, 0);
     48 
     49     setnonblocking(listenfd);
     50     ev.data.fd = listenfd;
     51     ev.events = EPOLLIN | EPOLLET;
     52     epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
     53     bzero(&serveraddr, sizeof(serveraddr));
     54     serveraddr.sin_family = AF_INET;
     55     char *local_addr = "192.168.1.63";
     56     inet_aton(local_addr, &(serveraddr.sin_addr));
     57     serveraddr.sin_port = htons(SERV_PORT);
     58     bind(listenfd, (sockaddr *)&serveraddr, sizeof(serveraddr));
     59     listen(listenfd, LISTENQ);
     60 
     61     maxi = 0;
     62     for( ; ; ) {
     63         nfds = epoll_wait(epfd, events, 20, 500);
     64         for(i=0;i<nfds;i++) {
     65             if(events[i].data.fd == listenfd) {
     66                 connfd = accept(listenfd, (sockaddr *)&clientaddr, &clilen);
     67                 if(connfd < 0) {
     68                     perror("connfd < 0");
     69                     exit(1);
     70                 }
     71                 setnonblocking(connfd);
     72                 char *str = inet_ntoa(clientaddr.sin_addr);
     73                 std::cout<<"connect from"<<str<<std::endl;
     74                 ev.data.fd = connfd;
     75                 ev.events = EPOLLIN | EPOLLET;
     76                 epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
     77             } else if(events[i].events & EPOLLIN) {
     78                 if((sockfd = events[i].data.fd) < 0) {
     79                     continue;
     80                 }
     81                 if((n = read(sockfd, line, MAXLINE)) < 0) {
     82                     if(errno == ECONNRESET) {
     83                         close(sockfd);
     84                         events[i].data.fd = -1;
     85                     } else {
     86                         std::cout<<"readline error"<<std::endl;
     87                     }
     88                 } else if(n == 0) {
     89                     close(sockfd);
     90                     events[i].data.fd = -1;
     91                 }
     92                 ev.data.fd = sockfd;
     93                 ev.events = EPOLLOUT | EPOLLET;
     94                 epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
     95             } else if(events[i].events & EPOLLOUT) {
     96                 sockfd = events[i].data.fd;
     97                 write(sockfd, line, n);
     98                 ev.data.fd = sockfd;
     99                 ev.events = EPOLLIN | EPOLLET;
    100                 epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
    101             }
    102         }
    103     }
    104 }
    epoll例子
    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    使用docker部署zabbix
    如何用好 IDEA ,Java 撸码效率至少提升 5 倍?
    getpass模块
    linux下利用nohup后台运行jar文件包程序
    Spring Cloud 与 Dubbo 区别
    git 打标签并推送tag到托管服务器
    git-stash用法小结
    git推送本地分支到远程分支
    Git dev分支合并到master分支完美实战
    IntelliJ远程调试教程
  • 原文地址:https://www.cnblogs.com/ch122633/p/8457782.html
Copyright © 2020-2023  润新知