一、epoll服务端实现中需要的3个函数:
- epoll_create:创建保存epoll文件描述符的空间。
- epoll_ctl:向空间注册并注销文件描述符。
- epoll_wait:与select函数类似,等待文件描述符发生变化。
二、示例
回声服务端:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <arpa/inet.h> 6 #include <sys/socket.h> 7 #include <sys/epoll.h> 8 9 #define BUF_SIZE 1024 10 #define EPOLL_SIZE 50 11 void error_handling(char * messages); 12 13 int main(int argc, char *argv[]) 14 { 15 if(argc != 2) 16 { 17 printf("Usage : %s <port> ", argv[0]); 18 exit(1); 19 } 20 int serverSock, clientSock; 21 struct sockaddr_in serverAddr, clientAddr; 22 socklen_t clientAddrSize; 23 24 char buf[BUF_SIZE]; 25 int strLen; 26 27 struct epoll_event *ep_events; 28 struct epoll_event event; 29 int epfd, event_cnt; 30 31 serverSock =socket(PF_INET, SOCK_STREAM, 0); 32 if(serverSock == -1) 33 error_handling("socket() error"); 34 35 memset(&serverAddr, 0, sizeof(serverAddr)); 36 serverAddr.sin_family = AF_INET; 37 serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); 38 serverAddr.sin_port = htons(atoi(argv[1])); 39 40 if(bind(serverSock, (struct sockaddr*) &serverAddr, sizeof(serverAddr)) == -1) 41 error_handling("bind() error"); 42 if(listen(serverSock, 5) == -1) 43 error_handling("listen() error"); 44 45 epfd = epoll_create(EPOLL_SIZE); 46 ep_events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * EPOLL_SIZE); 47 48 event.events = EPOLLIN; 49 event.data.fd = serverSock; 50 epoll_ctl(epfd, EPOLL_CTL_ADD, serverSock, &event); 51 52 puts("Server start..."); 53 while(1){ 54 event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1); 55 if(event_cnt == -1){ 56 puts("epoll_wait() error"); 57 break; 58 } 59 for(int i = 0; i < event_cnt; i++){ 60 if(ep_events[i].data.fd == serverSock){ 61 clientAddrSize = sizeof(clientAddr); 62 clientSock = accept(serverSock, (struct sockaddr*)&clientAddr, &clientAddrSize); 63 event.events = EPOLLIN; 64 event.data.fd = clientSock; 65 epoll_ctl(epfd, EPOLL_CTL_ADD, clientSock, &event); 66 printf("connected client: %d ", clientSock); 67 } 68 else{ 69 strLen = read(ep_events[i].data.fd, buf, BUF_SIZE); 70 if(strLen == 0){ 71 epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL); 72 close(ep_events[i].data.fd); 73 printf("closed client: %d ", ep_events[i].data.fd); 74 } 75 else{ 76 write(ep_events[i].data.fd, buf, strLen); 77 puts("echo"); 78 } 79 } 80 } 81 } 82 close(serverSock); 83 puts("Server close..."); 84 return 0; 85 } 86 87 void error_handling(char * messages) 88 { 89 puts(messages); 90 exit(1); 91 }
三、条件触发和边缘触发
条件触发方式中,只要输入缓冲有数据就会一直通知该事件。
边缘触发方式中输入缓冲收到数据时仅注册一次该事件。即使输入缓冲中还留有数据,也不会再进行注册。
select模型是以条件触发的方式工作的。
epoll默认是以条件触发方式工作的。
若将文件(套接字)改为非阻塞模式。调用read&write函数时,无论是否存在数据,都会形成非阻塞文件(套接字)。
边缘触发方式下,以阻塞方式工作的read&write函数又可能引起服务器端的长事件停顿。因此,边缘触发方式中一定要采用非阻塞read&write函数。
边缘触发的回声服务端:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <arpa/inet.h> 6 #include <sys/socket.h> 7 #include <sys/epoll.h> 8 #include <fcntl.h> 9 #include <errno.h> 10 11 #define BUF_SIZE 4 12 #define EPOLL_SIZE 50 13 void setnonblockingmode(int fd); 14 void error_handling(char * messages); 15 16 int main(int argc, char *argv[]) 17 { 18 if(argc != 2) 19 { 20 printf("Usage : %s <port> ", argv[0]); 21 exit(1); 22 } 23 int serverSock, clientSock; 24 struct sockaddr_in serverAddr, clientAddr; 25 socklen_t clientAddrSize; 26 27 char buf[BUF_SIZE]; 28 int strLen; 29 30 struct epoll_event *ep_events; 31 struct epoll_event event; 32 int epfd, event_cnt; 33 34 serverSock =socket(PF_INET, SOCK_STREAM, 0); 35 if(serverSock == -1) 36 error_handling("socket() error"); 37 38 memset(&serverAddr, 0, sizeof(serverAddr)); 39 serverAddr.sin_family = AF_INET; 40 serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); 41 serverAddr.sin_port = htons(atoi(argv[1])); 42 43 if(bind(serverSock, (struct sockaddr*) &serverAddr, sizeof(serverAddr)) == -1) 44 error_handling("bind() error"); 45 if(listen(serverSock, 5) == -1) 46 error_handling("listen() error"); 47 48 epfd = epoll_create(EPOLL_SIZE); 49 ep_events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * EPOLL_SIZE); 50 51 setnonblockingmode(serverSock); 52 event.events = EPOLLIN; 53 event.data.fd = serverSock; 54 epoll_ctl(epfd, EPOLL_CTL_ADD, serverSock, &event); 55 56 puts("Server start..."); 57 while(1){ 58 event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1); 59 if(event_cnt == -1){ 60 puts("epoll_wait() error"); 61 break; 62 } 63 puts("return epoll_wait"); 64 for(int i = 0; i < event_cnt; i++){ 65 if(ep_events[i].data.fd == serverSock){ 66 clientAddrSize = sizeof(clientAddr); 67 clientSock = accept(serverSock, (struct sockaddr*)&clientAddr, &clientAddrSize); 68 setnonblockingmode(clientSock); 69 event.events = EPOLLIN|EPOLLET; 70 event.data.fd = clientSock; 71 epoll_ctl(epfd, EPOLL_CTL_ADD, clientSock, &event); 72 printf("connected client: %d ", clientSock); 73 } 74 else{ 75 while(1) 76 { 77 strLen = read(ep_events[i].data.fd, buf, BUF_SIZE); 78 if(strLen == 0){ 79 epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL); 80 close(ep_events[i].data.fd); 81 printf("closed client: %d ", ep_events[i].data.fd); 82 break; 83 } 84 else if(strLen < 0){ 85 if(errno == EAGAIN) 86 break; 87 else 88 puts("???????"); 89 } 90 else{ 91 write(ep_events[i].data.fd, buf, strLen); 92 } 93 } 94 } 95 } 96 } 97 close(serverSock); 98 puts("Server close..."); 99 return 0; 100 } 101 102 void setnonblockingmode(int fd) // 套接字改成非阻塞的 103 { 104 int flag = fcntl(fd, F_GETFL, 0); 105 fcntl(fd, F_SETFL, flag|O_NONBLOCK); 106 } 107 108 void error_handling(char * messages) 109 { 110 puts(messages); 111 exit(1); 112 }