epoll 模型原理详解以及实例(2)

联合体epoll_data用来保存触发事件的某个文件描述符相关的数据。例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对应的socket文件描述符,可以把这文件描述符赋给epoll_data的fd字段,以便后面的读写操作在这个文件描述符上进行。

(2)epoll_create
函数声明:intepoll_create(intsize)
函数说明:该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围。

(3) epoll_ctl函数
函数声明:intepoll_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, structepoll_event * events, int maxevents, int timeout)
函数说明:该函数用于轮询I/O事件的发生。
epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传代处理事件的数组;
maxevents:每次能处理的事件数;
timeout:等待I/O事件发生的超时值;
返回发生事件数。

7 设计思路及模板
首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你的epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄,之后的所有操作都将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。
然后在你的网络主循环里面,调用epoll_wait(int epfd, epoll_event events, int max_events,int timeout)来查询所有的网络接口,看哪一个可以读,哪一个可以写。基本的语法为:
nfds = epoll_wait(kdpfd, events, maxevents, -1);
其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait函数操作成功之后,events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout参数指示 epoll_wait的超时条件,为0时表示马上返回;为-1时表示函数会一直等下去直到有事件返回;为任意正整数时表示等这么长的时间,如果一直没有事件,则会返回。一般情况下如果网络主循环是单线程的话,可以用-1来等待,这样可以保证一些效率,如果是和主循环在同一个线程的话,则可以用0来保证主循环的效率。epoll_wait返回之后,应该进入一个循环,以便遍历所有的事件。
对epoll 的操作就这么简单,总共不过4个API:epoll_create, epoll_ctl,epoll_wait和close。以下是man中的一个例子。

struct epoll_event ev, *events;
for(;;)
{
  nfds = epoll_wait(kdpfd, events, maxevents, -1);    //等待IO事件
  for(n = 0; n < nfds; ++n)
  {
  //如果是主socket的事件,则表示有新连接进入,需要进行新连接的处理。
      if(events[n].data.fd == listener)
      {
        client = accept(listener, (struct sockaddr *) &local,  &addrlen);
if(client < 0)
        {
            perror("accept error");
            continue;
        }
        // 将新连接置于非阻塞模式
        setnonblocking(client);
        ev.events = EPOLLIN | EPOLLET;
        //注意这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听,
        //如果有写操作的话,这个时候epoll是不会返回事件的,
        //如果要对写操作也监听的话,应该是EPOLLIN | EPOLLOUT | EPOLLET。
        // 并且将新连接也加入EPOLL的监听队列
        ev.data.fd = client;
        // 设置好event之后,将这个新的event通过epoll_ctl
        if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0)
        {
            //加入到epoll的监听队列里,这里用EPOLL_CTL_ADD
            //来加一个新的 epoll事件。可以通过EPOLL_CTL_DEL来减少
            //一个epoll事件,通过EPOLL_CTL_MOD来改变一个事件的监听方式。
            fprintf(stderr, "epoll set insertion error: fd=%d"0, client);
            return -1;
        }
      }
      else
      // 如果不是主socket的事件的话,则代表这是一个用户的socket的事件,
      // 则用来处理这个用户的socket的事情是,比如说read(fd,xxx)之类,或者一些其他的处理。
        do_use_fd(events[n].data.fd);
  }
}8 EPOLL模型的简单实例
#include <iostream>
#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>

#define MAXLINE 10
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 5555
#define INFTIM 1000

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/07056860132b8a0bd306cfbb0a535cbb.html