结构体epoll_event被用于注册所感兴趣的事件和回传所发生待处理的事件,定义如下:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;//保存触发事件的某个文件描述符相关的数据
struct epoll_event {
__uint32_t events;
epoll_data_t data;
};
其中events表示感兴趣的事件和被触发的事件,可能的取值为:
EPOLLIN:表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数可读;
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: ET的epoll工作模式;
epoll_data:
epoll_data中的fd是文件描述符
ptr提供了一个用户可用的指针
关于u32和u64是为了对齐,保证这个联合体长度为8个字节。
看了一下sys_epoll_x的实现,可以看到内核里epoll_event的定义
struct epoll_event {
__u32 events;
__u64 data;
}
因为ptr是一个指针,32位平台下指针长度u32,64BIT平台下指针长度是u64,这个是起到标识作用吧
所涉及到的函数有:
2、epoll_create函数
函数声明:int epoll_create(int size)
功能:该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围;
3、epoll_ctl函数
函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
功能:用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
@epfd:由epoll_create生成的epoll专用的文件描述符;
@op:要进行的操作,EPOLL_CTL_ADD注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除;
@fd:关联的文件描述符;
@event:指向epoll_event的指针;
成功:0;失败:-1
4、epoll_wait函数
函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
功能:该函数用于轮询I/O事件的发生;
@epfd:由epoll_create生成的epoll专用的文件描述符;
@epoll_event:用于回传代处理事件的数组;
@maxevents:每次能处理的事件数;
@timeout:等待I/O事件发生的超时值;
成功:返回发生的事件数;失败:-1
epoll sever 示例
//
// a simple echo server using epoll in linux
//
// author:sunny
//
// email: [email protected]
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVICE "epollserver"
#define BUFSIZE 1024
#define TIMEOUT 30
#define PORT 7888
#define STACKSIZE 81940
#define MAX_EVENTS 1024
#define MAX_THREAD 300
struct myepoll_data {
int fd;
int epollFd;
int epollFd2;
int len;
void *data;
};
static pthread_rwlock_t rwlock;
int iThreadCount;
int tootal=0;
int InitListenSocket(const char * service,short port);
void AcceptConn(int g_epollFd,int fd);
void RecvData(int g_epollFd,int g_sendFd,int fd);
void SendData(int g_sendFd,int fd, char* buf,int len);
void *writeThread(void *arg);
void *readThread(void *arg);
int main(int argc, char **argv)
{
int listenFd = InitListenSocket(SERVICE,PORT);
printf("server running:port[%d]\n", PORT);
int child =0;
int num_childs=5;
while(!child)
{
if(num_childs>0)
{
switch(fork())
{
case -1:
printf("error in fork!");
return -1;
case 0:
printf("i am the child process, my process id is %d\n",getpid());
child=1;
break;
default:
num_childs--;
break;
}
}
}
// fork 5 个子进程
// create epoll
struct epoll_event ev = {0, {0}};
int g_epollFd = epoll_create(MAX_EVENTS);
if(g_epollFd <= 0)
printf("create epoll failed.%d\n", g_epollFd);
int g_sendFd = epoll_create(MAX_EVENTS);
if(g_sendFd <= 0)
printf("create epoll failed.%d\n", g_sendFd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = listenFd;
if (epoll_ctl(g_epollFd, EPOLL_CTL_ADD,listenFd, &ev) < 0) {
printf("epoll set insertion error: fd=%d\n", listenFd);
exit(1);
}else
printf("listen socket add epoll complete!\n");
// event loop
struct epoll_event events[MAX_EVENTS];
iThreadCount = 0;
pthread_t tid,r_tid;
pthread_attr_t attr;
if ( pthread_attr_init(&attr) != 0)
return -1;
if(pthread_attr_setstacksize(&attr, STACKSIZE) != 0)
return -1;
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if(pthread_create(&r_tid,NULL,writeThread,(void *)g_sendFd)!=0)
{
printf("pthread_create writeThread error [%d]%s\n",errno,strerror(errno));
return -1;
}
//初始化读写锁
if (pthread_rwlock_init(&rwlock,NULL) != 0)
{
perror("rwlock initialization failed");
return -1;
}
while(1){
// wait for events to happen
int fds = epoll_wait(g_epollFd, events, MAX_EVENTS, -1);
if(fds < 0){
printf("epoll_wait error, exit\n");
break;
}
for(int i = 0; i < fds; i++){
if((events[i].data.fd==listenFd)&&(events[i].events&EPOLLIN)) // accept event
{
printf("AcceptConn Event,process:{%d] \n",getpid());
AcceptConn(g_epollFd,events[i].data.fd);
}else
if(events[i].events&EPOLLIN) // read event
{
printf("epoll read event[%d]\n",getpid());
while(iThreadCount>MAX_THREAD)
{
printf("pthread_create error [%d]\n",iThreadCount);
usleep(500);
}
struct myepoll_data* md = (myepoll_data*)events[i].data.ptr;
//RecvData(md->epollFd,md->fd);
md->epollFd2=g_sendFd;
if(pthread_create(&tid,&attr,readThread,(void *)md)!=0)
{
printf("pthread_create error [%d]%s\n",errno,strerror(errno));
}
}
}
}
// free resource
return 0;
}
// accept new connections from clients
void AcceptConn(int g_epollFd,int fd)
{
struct sockaddr_in sin;
socklen_t len = sizeof(struct sockaddr_in);
int nfd, i;
// accept
struct epoll_event evt = {0, {0}};
while(1)
{
nfd = accept(fd, (struct sockaddr*)&sin, &len);
if(nfd==-1)
{
if(errno == EAGAIN)//没有连接需要接收了
{
//printf("%s: accept over,EAGAIN: %d\n", __func__, errno);
break;
}else
{
printf("%s: accept error,errno: %d\n", __func__, errno);
}
}
// set nonblocking
if(( fcntl(nfd, F_SETFL, O_NONBLOCK)) < 0)
{
printf("%s: fcntl nonblocking failed:%d\n", __func__, errno);
break;
}
struct myepoll_data* md =(struct myepoll_data*)malloc(sizeof(struct myepoll_data));
md->fd=nfd;
md->epollFd=g_epollFd;
evt.data.ptr=md;
evt.events=EPOLLIN|EPOLLET;
if(epoll_ctl(g_epollFd,EPOLL_CTL_ADD,nfd, &evt)<0)
{
printf("accept EPOLL_CTL_ADD failed:%d\n", errno);
}
}
}
// receive data
void RecvData(int g_epollFd,int g_sendFd,int fd)
{
int len;
// receive data
char buf[BUFSIZ];
struct epoll_event evt = {0, {0}};
int pos = 0;
while (1)
{
len = read(fd, buf+pos,sizeof(buf)-pos);
if(len > 0)
{
//printf("[recv],len:%d\n",len);
pos += len;
continue ;
}
else if ( len < 0 )
{
//printf("[recv] :ret[%d] errno [%d]\n",len,errno);
if ( errno == EAGAIN ){
//printf("[recv] complete,len:%d[%s]\n",pos,buf);
break;
}
else if ( errno == EINTR ) continue ;
else {
////同一连接可能会出现两个客户端主动关闭的事件,一个errno是EAGAIN(11),一个errno是EBADF(9)
printf("[recv],some error occurs:%d %s\n",errno,strerror(errno));
break;}
// some error occurs
}
else // ( len == 0 )
{
printf("client disconnect\n");
epoll_ctl(g_epollFd,EPOLL_CTL_DEL,fd, &evt);
close(fd);
return ;
// client disconnect
}
}
epoll_ctl(g_epollFd,EPOLL_CTL_DEL,fd, &evt);
len=pos;
if(len > 0)
{
sprintf(buf,"HTTP/1.0 200 OK\r\nContent-type: text/plain\r\n\r\n%s","Hello world!\n");
sleep(3);
struct myepoll_data* md =(struct myepoll_data*)malloc(sizeof(struct myepoll_data));
md->fd=fd;
md->data=buf;
md->epollFd=g_epollFd;
md->len=strlen(buf);
evt.data.ptr=md;
evt.events=EPOLLOUT|EPOLLET;
if(epoll_ctl(g_sendFd,EPOLL_CTL_ADD,fd, &evt)<0)
{
printf("SendData EPOLL_CTL_MOD failed:%d\n", errno);
}
}
}
void SendData(int g_epollFd,int fd, char* buf,int len)
{
//printf("[SendData] [%s]\n",buf);
int retval,ret;
int pos = len;
while(pos>0)
{
ret = send(fd, buf+len-pos, pos, 0);
if(ret>0)
{
retval += ret;
pos -= ret;
}else
if(ret < 0)
{
//收到中断信号,可以继续写,或者等待后续epoll通知,再写。
if(errno == EINTR)
break;
//写缓冲队列已满,等待再写。
if(errno == EAGAIN)
{
usleep(500);
continue;
}
printf("[SendData errno [%d]%s\n",errno,strerror(errno));
break;
}
if(retval == len||pos==0)
{
//printf("[SendData complete,len:%d][%s]\n",len,buf);
break;
}
}
struct epoll_event evt = {0, {0}};
tootal++;
printf("disconnect [%d][%d]\n",getpid(),tootal);
epoll_ctl(g_epollFd,EPOLL_CTL_DEL,fd, &evt);
close(fd);
}
void *readThread(void *arg)
{
pthread_rwlock_wrlock(&rwlock);//获取读取锁
iThreadCount++;
printf("workThread start %d\n",iThreadCount);
pthread_rwlock_unlock(&rwlock);
struct myepoll_data* md =(struct myepoll_data*)arg;
RecvData(md->epollFd,md->epollFd2,md->fd);
pthread_rwlock_wrlock(&rwlock);//获取读取锁
iThreadCount--;
pthread_rwlock_unlock(&rwlock);
pthread_exit(NULL);
}
void *writeThread(void *arg)
{
int g_sendFd= (int)arg;
struct epoll_event events[MAX_EVENTS];
while(1){
// wait for events to happen
int fds = epoll_wait(g_sendFd, events, MAX_EVENTS, 500);
if (fds == -1)
{
if(errno==EINTR)
{
continue;
}
printf("[epoll_wait2]errno=%d,%s\r\n",errno,strerror(errno));
break;
}
else if(fds==0)
{
continue;
}
for(int i = 0; i < fds; i++){
if(events[i].events&EPOLLOUT) // write event
{
struct myepoll_data* md = (myepoll_data*)events[i].data.ptr;
//printf("epoll write event [%d][%d]\n",md->fd,events[i].data.fd);
SendData(md->epollFd,md->fd,(char *)md->data,md->len);
}
}
}
// free resource
pthread_exit(NULL);
}
int InitListenSocket(const char * service,short port)
{
struct servent *pse;
struct protoent *ppe;
struct sockaddr_in sin;
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
if (pse = getservbyname(service, "tcp"))
sin.sin_port = htons(port);//ntohs((u_short)pse->s_port));
else if((sin.sin_port = htons(port)) == 0)
exit(1);//errexit("can't get service\n");
if ((ppe = getprotobyname("tcp")) == 0)exit(1);
//errexit("can't get protocol entry\n");
int listenFd = socket(AF_INET, SOCK_STREAM, 0);
if(listenFd==-1)
{
perror("socket");
exit(1);
}
fcntl(listenFd, F_SETFL, O_NONBLOCK); // set non-blocking
if(bind(listenFd, (const sockaddr*)&sin, sizeof(sin))==-1)
{
perror("bind");
exit(1);
}
if(listen(listenFd, 50)==-1)
{
perror("listen");
exit(1);
}
printf("server listen fd=%d\n", listenFd);
return listenFd;
}