嵌入式 Linux网络编程(五)——epoll机制

一、epoll简介

        epoll是在2.6内核中提出的,是selectpoll的增强版本。epoll更加灵活,没有描述符限制使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中。

1epoll函数

#include <sys/epoll.h>

int epoll_create(int size);

    创建一个epoll的句柄,size表示监听的文件描述的数量

int epoll_ctl(int epfd, int op, int size, struct epoll_event *event);

    epoll的事件注册函数,先注册要监听的事件类型。epfd是epoll_create()的返回值,op参数表示动作,用三个宏来表示:

        EPOLL_CTL_ADD:注册新的fdepfd中;
        EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
        EPOLL_CTL_DEL:从epfd中删除一个fd
    fd参数是需要监听的fd,event参数是告诉内核需要监听什么事

struct epoll_event {

               __uint32_t   events;      /* Epoll events */

               epoll_data_t data;        /* User data variable */

           };

        EPOLLIN :表示对应的文件描述符可以读

        EPOLLOUT:表示对应的文件描述符可以写

        EPOLLPRI:表示文件描述符有紧急的数据可读

        EPOLLERR:表示文件描述符发生错误

        EPOLLHUP:表示文件描述符被挂断
        EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式

        EPOLLONESHOT:只监听一次事件

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

    等待事件的产生,类似于select()调用。

    参数events用来从内核得到事件的集合

    maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size

    参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。

    函数返回需要处理的事件数目,如返回0表示已超时。

2、epoll工作模式

        epoll对文件描述符的操作有两种模式:LTlevel trigger)和ETedge trigger)。LT模式是默认模式

        LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。

        ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。

        ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

二、epoll编程模型

epoll编程的基本框架:

while(1){      nfds = epoll_wait(epfd,events,20,500);      for(i=0;i
fd;                        send( sockfd, md->ptr, strlen((char*)md->ptr),0 );//发送数据              ev.data.fd=sockfd;                         ev.events=EPOLLIN|EPOLLET;                         epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);           }                  else                  {                     //其他的处理                  }         }//end of for}//end of while

程序实例:

server.c:

#include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
 #include 
#include 
#include 
#include 
 #define PORT 8888#define RT_ERR (-1)#define RT_OK 0#define SERVERIP "192.168.0.200"#define LISTEN_QUEUE 10#define BUFFER_SIZE 1024#define MAX_EVENTS 1024 int main(int argc, char *argv[]){    int listenfd, connsockfd, fd, len;    char readbuf[BUFFER_SIZE];    listenfd = socket(AF_INET, SOCK_STREAM, 0);    if(listenfd < 0)    {            fprintf(stderr, "socket function failed.\n");            exit(RT_ERR);    }        struct sockaddr_in serveraddr, clientaddr;//    bzero(&clientaddr, sizeof(clientaddr));    bzero(&serveraddr, sizeof(serveraddr));    serveraddr.sin_family = AF_INET;    serveraddr.sin_port = htons(PORT);    serveraddr.sin_addr.s_addr = inet_addr(SERVERIP);        unsigned int client_len = sizeof(struct sockaddr_in);    if(bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)    {            fprintf(stderr, "bind function failed.\n");            close(listenfd);            exit(RT_ERR);    }       if(listen(listenfd,LISTEN_QUEUE) < 0)    {            fprintf(stderr, "listen function failed.\n");            close(listenfd);        exit(RT_ERR);    }    int opts=fcntl(listenfd, F_GETFL);      if(opts<0)      {         fprintf(stderr, "fcntl(sock,GETFL)\n");         exit(-1);      }        opts = opts|O_NONBLOCK;      if(fcntl(listenfd,F_SETFL,opts)<0)      {         fprintf(stderr, "fcntl(sock,SETFL,opts)\n");         exit(-1);      }      fprintf(stdout, "The server IP is %s, listen on port: %d\n", inet_ntoa(serveraddr.sin_addr), ntohs(serveraddr.sin_port));       struct epoll_event event, events[MAX_EVENTS];    int epfd = epoll_create(MAX_EVENTS);    event.data.fd=listenfd;      event.events=EPOLLIN|EPOLLET;     epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&event);          int nepfd, n;    while(1)    {        nepfd = epoll_wait(epfd, events, MAX_EVENTS, 0);        for(fd = 0; fd < nepfd; fd++)        {            bzero(&clientaddr, sizeof(clientaddr));            fprintf(stdout, "fd is %d, listenfd is %d\n", fd, listenfd);            if(events[fd].data.fd == listenfd)//新的请求连接用户            {                    connsockfd = accept(listenfd, (struct sockaddr *)&clientaddr, &len);                if(connsockfd < 0)                {                    fprintf(stderr, "accept function failed.\n");                    exit(RT_ERR);                }                fprintf(stdout, "accept a new session from %s port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));                    event.data.fd = connsockfd;                event.events = EPOLLIN|EPOLLET;                 epoll_ctl(epfd, EPOLL_CTL_ADD, connsockfd, &event);             }            else if(events[fd].events&EPOLLIN)//如果是已连接用户,有数据接收            {                struct sockaddr_in connaddr;                int connlen;                if((connsockfd = events[fd].data.fd) < 0)                    continue;                if((n = recv(connsockfd, readbuf, sizeof(readbuf), 0)) < 0)                 {                       if (errno == ECONNRESET)                     {                         close(connsockfd);                         events[fd].data.fd = -1;                       }                     else                        fprintf(stderr, "recv function failed.\n");                     }                      else if(n == 0)                     {                        close(connsockfd);                          events[fd].data.fd = -1;                        }                 fprintf(stdout, "mesage: %s", readbuf);                 event.data.fd = connsockfd;                          event.events = EPOLLOUT|EPOLLET;              }            else if(events[fd].events&EPOLLOUT)//有数据发送            {                connsockfd = events[fd].data.fd;                       send(connsockfd, readbuf, sizeof(readbuf), 0);                       event.data.fd = connsockfd;                       event.events=EPOLLIN|EPOLLET;                        epoll_ctl(epfd, EPOLL_CTL_MOD, connsockfd, &event);                }        }//end of for    }//end of while    return 0;}

client.c:

#include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
 #include 
 #define SERVERIP "192.168.0.200"#define PORT 8888#define BUFFER_SIZE 512 int main(int argc, char *argv[]){    int sockfd;    struct sockaddr_in server;    char sendbuf[BUFFER_SIZE];    bzero(&sendbuf,sizeof(sendbuf));    sockfd = socket(AF_INET,SOCK_STREAM,0);    bzero(&server,sizeof(server));    server.sin_family = AF_INET;    server.sin_port = htons(PORT);    server.sin_addr.s_addr = inet_addr(SERVERIP);         if(connect(sockfd,(struct sockaddr *)&server,sizeof(struct sockaddr)) < 0)    {            perror("connect failed.\n");            return -1;    }    while(1)    {         fgets(sendbuf, sizeof(sendbuf), stdin);        send(sockfd, sendbuf, sizeof(sendbuf), 0);        bzero(&sendbuf,sizeof(sendbuf));     }    close(sockfd);    return 0;}