在网络编程的多路复用模型中传统的select只能监视一定数量的套接字,poll没有这个限制,但也必须线性的扫描监视的套接字集中各个套接字的状态,而epoll模型则不存在这个问题。函数返回制定时间内由状态变化的套接字个数N,对应套接字集合中的前N个套接字。epoll分为2种工作模式,
level-triggered模式下,只要有数据没有接受都会随epoll_wait函数返回,edge-triggered模式下则只提示一次,即可能需要多次调用recv函数直到确定没有数据要接受。
epoll模型主要用到3个函数,epoll_create函数用于创建一个epoll描述符,epoll_ctl函数用于添加移除变更epoll描述符对应的套接字描述符,epoll_wait函数用于等待监控的套接字字符集由事件发生,或者超时返回。以下是一个回显采用epoll模型服务器的例子。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>
#define MAX 20000
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;
}
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;
hints.ai_socktype=SOCK_STREAM;
hints.ai_flags=AI_PASSIVE;
s=getaddrinfo(NULL,port,&hints,&result);
if(s!=0)
{
perror("getaddrinfo");
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)
break;
close(sfd);
}
if(rp==NULL)
{
perror("bind");
return -1;
}
freeaddrinfo(result);
return sfd;
}
int
main(int argc,char *argv[])
{
int sfd,s;
int efd;
struct epoll_event event;
struct epoll_event *events;
if(argc!=2)
{
perror("argc");
exit(EXIT_FAILURE);
}
sfd=create_and_bind(argv[1]);
if(sfd==-1)
abort();
s=make_socket_non_blocking(sfd);
if(s==-1)
abort();
s=listen(sfd,128);
if(s==-1)
{
perror("listen");
return -1;
}
efd=epoll_create1(0);
if(efd==-1)
{
perror("epoll_create");
abort();
}
event.data.fd=sfd;
event.events=EPOLLIN|EPOLLET;
s=epoll_ctl(efd,EPOLL_CTL_ADD,sfd,&event);
if(s==-1)
{
perror("epoll_ctl");
abort();
}
events=calloc(MAX,sizeof(event));
while(1)
{
int n,i;
n=epoll_wait(efd,events,MAX,-1);
for(i=0;i<n;++i)
{
if((events[i].events&EPOLLERR)||
(events[i].events&EPOLLHUP)||
(!(events[i].events&EPOLLIN)))
{
perror("epoll error");
close(events[i].data.fd);
continue;
}
else if(sfd==events[i].data.fd)
{
while(1)
{
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[1024],sbuf[1024];
in_len=sizeof(in_addr);
infd=accept(sfd,&in_addr,&in_len);
if(infd==-1)
{
if((errno==EAGAIN)||
(errno==EWOULDBLOCK))
break;
else
{
perror("accept");
break;
}
}
s=getnameinfo(&in_addr,in_len,hbuf,sizeof(hbuf),
sbuf,sizeof(sbuf),NI_NUMERICSERV|NI_NUMERICHOST);
if(s==0)
{
printf("accepted connection on des %d,host=%s,port=%s\n",infd,hbuf,sbuf);
}
s=make_socket_non_blocking(infd);
if(s==-1)
abort();
event.data.fd=infd;
event.events=EPOLLIN|EPOLLET;
s=epoll_ctl(efd,EPOLL_CTL_ADD,infd,&event);
if(s==-1)
{
perror("epoll_ctl");
abort();
}
}
continue;
}
else
{
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)
{
perror("read");
done=1;
}
break;
}
else if (count==0)
{
done=1;
break;
}
s=write(1,buf,count);
if(s==-1)
{
perror("write");
abort();
}
}
if(done)
{
printf("close connection on des %d\n",events[i].data.fd);
close(events[i].data.fd);
}
}
}
}
free(events);
close(sfd);
return EXIT_SUCCESS;
}