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例子

 

posted @ 2018-02-21 23:26  习惯就好233  阅读(168)  评论(0编辑  收藏  举报