創建一個epoll句柄,參數size用來告訴內核監聽的文件描述符的個數,跟內存大小有關。
#include <sys/epoll.h>
int epoll_create(int size) size:監聽數目? ? ?通過創建一個size大小的紅黑數來實現epoll句柄,返回epfd是該樹的根節點。
?
控制某個epoll監控的文件描述符上的事件:注冊、修改、刪除。
?
#include <sys/epoll.h>
?
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
?
epfd: 為epoll_creat的句柄
?
op: 表示動作,用3個宏來表示:
?
EPOLL_CTL_ADD?(注冊新的fd到epfd),
?
EPOLL_CTL_MOD?(修改已經注冊的fd的監聽事件),
?
EPOLL_CTL_DEL?(從epfd刪除一個fd);
?
event: 告訴內核需要監聽的事件,是一個地址
struct epoll_event {
__uint32_t events; //EPOLLIN EPOLLOUT EPOLLERR
epoll_data_t data;?
};
typedef union epoll_data {
void *ptr;
int fd; //相當于函數中使用的fd
uint32_t u32;
uint64_t u64;
} epoll_data_t;
?
等待所監控文件描述符上有事件的產生,類似于select()調用。
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
events: 用來存內核得到事件的集合,是一個數組并且是傳出參數。
maxevents: 告之內核這個events有多大,這個maxevents的值不能大于創建epoll_create()時的size,
timeout: 是超時時間
-1: 阻塞
0: 立即返回,非阻塞
>0: 指定毫秒
返回值: 成功返回有多少文件描述符就緒,時間到時返回0,出錯返回-1
?
示例:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/epoll.h> #include <errno.h> #include <ctype.h>#include "wrap.h"#define MAXLINE 8192 #define SERV_PORT 8000 #define OPEN_MAX 5000int main(int argc, char *argv[]) {int i, listenfd, connfd, sockfd;int n, num = 0;ssize_t nready, efd, res;char buf[MAXLINE], str[INET_ADDRSTRLEN];socklen_t clilen;struct sockaddr_in cliaddr, servaddr;struct epoll_event tep, ep[OPEN_MAX]; //tep: epoll_ctl參數 ep[] : epoll_wait參數 listenfd = Socket(AF_INET, SOCK_STREAM, 0);int opt = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //端口復用 bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);Bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));Listen(listenfd, 20);efd = epoll_create(OPEN_MAX); //創建epoll模型, efd指向紅黑樹根節點if (efd == -1)perr_exit("epoll_create error");tep.events = EPOLLIN; tep.data.fd = listenfd; //指定lfd的監聽時間為"讀"res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep); //將lfd及對應的結構體設置到樹上,efd可找到該樹if (res == -1)perr_exit("epoll_ctl error");for ( ; ; ) {/*epoll為server阻塞監聽事件, ep為struct epoll_event類型數組, OPEN_MAX為數組容量, -1表永久阻塞*/nready = epoll_wait(efd, ep, OPEN_MAX, -1); if (nready == -1)perr_exit("epoll_wait error");for (i = 0; i < nready; i++) {if (!(ep[i].events & EPOLLIN)) //如果不是"讀"事件, 繼續循環continue;if (ep[i].data.fd == listenfd) { //判斷滿足事件的fd是不是lfd clilen = sizeof(cliaddr);connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen); //接受鏈接 printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));printf("cfd %d---client %d\n", connfd, ++num);tep.events = EPOLLIN; tep.data.fd = connfd;res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);if (res == -1)perr_exit("epoll_ctl error");} else { //不是lfd, sockfd = ep[i].data.fd;n = Read(sockfd, buf, MAXLINE);if (n == 0) { //讀到0,說明客戶端關閉鏈接res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL); //將該文件描述符從紅黑樹摘除if (res == -1)perr_exit("epoll_ctl error");Close(sockfd); //關閉與該客戶端的鏈接printf("client[%d] closed connection\n", sockfd);} else if (n < 0) { //出錯perror("read n < 0 error: ");res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);Close(sockfd);} else { //實際讀到了字節數for (i = 0; i < n; i++)buf[i] = toupper(buf[i]); //轉大寫,寫回給客戶端 Write(STDOUT_FILENO, buf, n);Writen(sockfd, buf, n);}}}}Close(listenfd);Close(efd);return 0; }
?