epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
目前epell是linux大规模并发网络程序中的热门首选模型。
epoll除了提供select/poll那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
server_epoll.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <sys/epoll.h>
#define MAXLINE 80
#define SERV_PORT 6666
#define OPEN_MAX 1024
int main()
{
//1.创建socket
int lfd, cfd, efd,sockfd;
int i, j, n, res, nready;
struct sockaddr_in serv_addr, clnt_addr;
socklen_t clnt_len;
struct epoll_event tep, ep[OPEN_MAX];
char rbuf[MAXLINE];
lfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
//2.设置端口复用
int opt;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(lfd, 128);
//5.epoll_create, epoll_ctl, epoll_wait
//创建红黑树
efd = epoll_create(OPEN_MAX);
tep.events = EPOLLIN; tep.data.fd = lfd;
//将lfd挂载到efd树上
res = epoll_ctl(efd, EPOLL_CTL_ADD, lfd, &tep);
if(res == -1){
perror("epoll_ctl error");
exit(1);
}
while(1){
//等待红黑树上的fd事件就绪,所有返回的事件存储在ep中
nready = epoll_wait(efd, ep, OPEN_MAX, -1);
if(nready == -1){
perror("epoll_wait error");
exit(1);
}
for(i = 0; i < nready; i++){
if(!ep[i].events & EPOLLIN){ //不是读事件
continue;
}
if(ep[i].data.fd == lfd){ //新连接
clnt_len = sizeof(clnt_addr);
cfd = accept(lfd, (struct sockaddr*)&serv_addr, &clnt_len);
tep.events = EPOLLIN; tep.data.fd = cfd;
//将cfd挂载到efd树上
res = epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &tep);
if(res == -1){
perror("epoll_ctl error");
exit(1);
}
}else { //不是新连接
sockfd = ep[i].data.fd;
n = read(sockfd, rbuf, sizeof(rbuf));
if(n == 0){ //客户端关闭连接
printf("客户端关闭连接...\n");
close(sockfd);
//将sockfd从efd树上摘除
res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, &tep);
}else if(n < 0){
printf("read n < 0 error...\n");
close(sockfd);
res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, &tep);
}else if(n > 0){
for(j = 0; j < n; j++){
rbuf[j] = toupper(rbuf[j]);
}
write(sockfd, rbuf, n);
write(STDOUT_FILENO, rbuf, n);
}
}
}
}
close(lfd);
close(efd);
}