linux epoll

1. struct epoll_event

 结构体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: gkyh@outlook.com

//   

#include <sys/signal.h>

#include <sys/socket.h>  

#include <sys/epoll.h>  

#include <netinet/in.h>  

#include <arpa/inet.h>  

#include <fcntl.h>  

#include <unistd.h>  

#include <stdio.h>  

#include <errno.h>  

#include <iostream> 

#include <string.h>

#include <stdlib.h>

#include <pthread.h>

#include <sys/wait.h>

#include <sys/types.h>

#include <sys/time.h>

#include <sys/resource.h>

#include <sys/ipc.h>

#include <sys/sem.h>

#include <sys/msg.h>

#include <netdb.h>

#include <unistd.h>

#include <stdarg.h>

#include <time.h>

#include <fcntl.h>



#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;

   

}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值