epoll

epoll相关的api:

/* 创建一个epoll实例。返回一个实例的fd。"size"参数是该epoll实例关联
   文件描述符数量的暗示。epoll_create()返回的fd应该用close()关闭。 */
extern int epoll_create (int __size) __THROW;

/* 把"fd"文件描述符,注册到"epfd" epoll实例。
   "op"参数是EPOLL_CTL_*常量,"event"参数描述调用者感兴趣的事件。 */
extern int epoll_ctl (int __epfd, int __op, int __fd,
              struct epoll_event *__event) __THROW;

//每次调用epoll_wait,发生了事件的描述符会保存在events数组中。
/* 等待"epfd" epoll实例上的事件。返回值是"events"数组中触发事件的数量
   "events"参数是包含触发事件的数组。"maxevents"是返回的最大的触发事件
   的个数,通常是"events"的大小。"timeout"是等待的最大时间,-1表示无限。 */
extern int epoll_wait (int __epfd, struct epoll_event *__events,
               int __maxevents, int __timeout);

               
/* Valid opcodes ( "op" parameter ) to issue to epoll_ctl().  */
#define EPOLL_CTL_ADD 1    
#define EPOLL_CTL_DEL 2    
#define EPOLL_CTL_MOD 3    
               
struct epoll_event
{
  uint32_t events;    /* Epoll events */
  epoll_data_t data;    /* User data variable */
} __EPOLL_PACKED;

typedef union epoll_data
{
  void *ptr;
  int fd;
  uint32_t u32;
  uint64_t u64;
} epoll_data_t;

网上搜集的epoll代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <errno.h>
#include <netdb.h>
#include <fcntl.h>
#include <string.h>

#define MAXEVENTS 64

static int create_and_bind(char*);
static int make_socket_non_blocking(int);

int main(int argc, char *argv[]) {
    int listenfd, s;
    int efd;
    struct epoll_event event;
    struct epoll_event *events;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s [port]
", argv[0]);
        exit(EXIT_FAILURE);
    }

    listenfd = create_and_bind(argv[1]);
    if (listenfd == -1)
        abort();

    s = make_socket_non_blocking(listenfd);
    if (s == -1)
        abort();

    s = listen(listenfd, SOMAXCONN);
    if (s == -1) {
        perror("listen");
        abort();
    }

    //首先得到一个epoll实例标识符
    efd = epoll_create1(0);
    if (efd == -1) {
        perror("epoll_create");
        abort();
    }

    event.data.fd = listenfd;
    event.events = EPOLLIN | EPOLLET;
    //listenfd和&event是一对,表示我们所关心的套接字的事件,并注册到epoll实例
    s = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &event);
    if (s == -1) {
        perror("epoll_ctl");
        abort();
    }

    /* Buffer where events are returned */
    events = calloc(MAXEVENTS, sizeof event);

    /* The event loop */
    while (1) {
        int n, i;

        n = epoll_wait(efd, events, MAXEVENTS, -1);
        for (i = 0; i < n; i++) {
            if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP)
                    || (!(events[i].events & EPOLLIN))) {
                /* An error has occured on this fd, or the socket is not
                 ready for reading (why were we notified then?) */
                fprintf(stderr, "epoll error
");
                close(events[i].data.fd);
                continue;
            } else if (listenfd == events[i].data.fd) {
                /* We have a notification on the listening socket, which
                 means one or more incoming connections. */
                while (1) {
                    struct sockaddr clientaddr;
                    socklen_t addrlen;
                    int connfd;
                    char hostbuf[NI_MAXHOST], portbuf[NI_MAXSERV];

                    addrlen = sizeof clientaddr;
                    connfd = accept(listenfd, &clientaddr, &addrlen);
                    if (connfd == -1) {
                        if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                            /* We have processed all incoming
                             connections. */
                            break;
                        } else {
                            perror("accept");
                            break;
                        }
                    }

                    s = getnameinfo(&clientaddr, addrlen, hostbuf, sizeof hostbuf, portbuf, sizeof portbuf,
                            NI_NUMERICHOST | NI_NUMERICSERV);
                    if (s == 0) {
                        printf("Accepted connection on descriptor %d "
                                "(host=%s, port=%s)
", connfd, hostbuf, portbuf);
                    }

                    /* Make the incoming socket non-blocking and add it to the
                     list of fds to monitor. */
                    s = make_socket_non_blocking(connfd);
                    if (s == -1)
                        abort();

                    //新产生的套接字,仍然需要注册到epoll实例
                    event.data.fd = connfd;
                    event.events = EPOLLIN | EPOLLET;
                    s = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);
                    if (s == -1) {
                        perror("epoll_ctl");
                        abort();
                    }
                }
                continue;
            } else {
                /* We have data on the fd waiting to be read. Read and
                 display it. We must read whatever data is available
                 completely, as we are running in edge-triggered mode
                 and won't get a notification again for the same
                 data. */
                int done = 0;

                while (1) {
                    ssize_t count;
                    char buf[512];

                    count = read(events[i].data.fd, buf, sizeof buf);
                    if (count == -1) {
                        /* If errno == EAGAIN, that means we have read all
                         data. So go back to the main loop. */
                        if (errno != EAGAIN) {
                            perror("read");
                            done = 1;
                        }
                        break;
                    } else if (count == 0) {
                        /* End of file. The remote has closed the
                         connection. */
                        done = 1;
                        break;
                    }

                    /* Write the buffer to standard output */
                    s = write(1, buf, count);
                    if (s == -1) {
                        perror("write");
                        abort();
                    }
                }

                if (done) {
                    printf("Closed connection on descriptor %d
",
                            events[i].data.fd);

                    /* Closing the descriptor will make epoll remove it
                     from the set of descriptors which are monitored. */
                    close(events[i].data.fd);
                }
            }
        }
    }

    free(events);

    close(listenfd);

    return EXIT_SUCCESS;
}

static int create_and_bind(char *port) {
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int s, sfd;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */
    hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */
    hints.ai_flags = AI_PASSIVE; /* All interfaces */

    s = getaddrinfo(NULL, port, &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s
", gai_strerror(s));
        return -1;
    }

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (sfd == -1)
            continue;
        s = bind(sfd, rp->ai_addr, rp->ai_addrlen);
        if (s == 0) {
            /* We managed to bind successfully! */
            break;
        }

        close(sfd);
    }
    if (rp == NULL) {
        fprintf(stderr, "Could not bind
");
        return -1;
    }
    freeaddrinfo(result);
    return sfd;
}

static int make_socket_non_blocking(int sfd) {
    int flags, s;

    flags = fcntl(sfd, F_GETFL, 0);
    if (flags == -1) {
        perror("fcntl");
        return -1;
    }

    flags |= O_NONBLOCK;
    s = fcntl(sfd, F_SETFL, flags);
    if (s == -1) {
        perror("fcntl");
        return -1;
    }

    return 0;
}
原文地址:https://www.cnblogs.com/allenwas3/p/7977220.html