epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被监听的文件描述符集合,另一个原因就是获取事件的时候,它无需遍历整个被监听的描述符集,只要遍历那些被IO事件唤醒而加入ready队列的描述符即可。
epoll的server端:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<unistd.h> 5 #include<errno.h> 6 #include<pthread.h> 7 #include<ctype.h> 8 #include<sys/socket.h> 9 #include<sys/epoll.h> 10 #include<arpa/inet.h> 11 12 #define SERV_PORT 9527 13 #define MAXLINE 8192 14 #define OPEN_MAX 5000 15 16 int main(int argc,char *argv[]) 17 { 18 int listenfd = 0,connfd = 0,sockfd,i; 19 int n,num = 0; 20 ssize_t nready,efd,res; 21 char buf[MAXLINE], str[INET_ADDRSTRLEN]; 22 socklen_t clilen; 23 24 struct sockaddr_in cliaddr,servaddr; 25 26 listenfd = socket(AF_INET,SOCK_STREAM,0); 27 int opt = 1; 28 setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); //端口复用 29 memset(&servaddr,0,sizeof(servaddr)); 30 servaddr.sin_family = AF_INET; 31 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 32 servaddr.sin_port = htons(SERV_PORT); 33 bind(listenfd,(struct sockaddr *)&servaddr, sizeof(servaddr)); 34 listen(listenfd,20); 35 36 efd = epoll_create(OPEN_MAX); //创建epoll模型,efd指向红黑树根节点 37 38 struct epoll_event tep, ep[OPEN_MAX]; //tep:epoll_ctl参数,ep[]:epoll_wait参数; 39 tep.events = EPOLLIN; 40 tep.data.fd = listenfd; //指定listenfd的监听事件为“读” 41 42 res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep); //将listenfd及对应的结构体设置到树上,efd可找到该树 43 44 for(;;){ 45 //epoll为server阻塞监听事件,ep为struct epoll_event类型数组,OPEN_MAX为数组容量,-1表示永久阻塞 46 nready = epoll_wait(efd,ep,OPEN_MAX,-1); 47 48 for(i=0;i<nready;i++){ 49 if(!(ep[i].events & EPOLLIN)) 50 continue; 51 if(ep[i].data.fd == listenfd){ //判断满足事件的fd是不是lfd 52 clilen = sizeof(cliaddr); 53 connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&clilen); //接受连接 54 55 tep.events = EPOLLIN; 56 tep.data.fd = connfd; 57 58 res = epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep); //加入红黑树 59 }else{ //不是lfd 60 sockfd = ep[i].data.fd; 61 n = read(sockfd,buf,MAXLINE); 62 63 if(n == 0){ //读到0,说明客户端关闭连接 64 res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL); //将该文件描述符从红黑树摘除 65 close(sockfd); //关闭与该客户端的连接 66 } 67 else if(n < 0){ //出错 68 res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL); //摘除节点 69 close(sockfd); 70 } 71 else{ //读到了字节数 72 for(i=0;i<n;i++) 73 buf[i] = toupper(buf[i]); 74 75 write(STDOUT_FILENO,buf,n); 76 write(sockfd,buf,n); 77 } 78 } 79 } 80 } 81 close(listenfd); 82 return 0; 83 }