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; }