一、epoll_create

#include <sys/epoll.h>

int epoll_create(int size);
int epoll_create1(int flags);
返回:成功非负文件描述符,-1出错
size:内核监听数目一共多大

创建一个epoll接口,size参数和select不同,不是fd+1?

需要注意的是:当创建好epoll后,它就会占用一个fd值,在linux /proc/id/fd/能看到这个fd的,所以使用完epoll后,必须close()关闭,否则可能导致耗尽fd。

二、epoll_ctl

#include <sys/epoll.h>

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
返回:0成功,-1失败
epfd:由epoll_create生成的epoll专用的文件描述符
op:要进行的操作,可能取EPOLL_CTL_ADD注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除
fd:关联的文件描述符
event:指向epoll_event指针

epoll的相关的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 events */
epoll_data_t data; /* User data variable */
};

其中的events可以是以下几个宏的集合:

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

三、epoll_wait

#include <sys/epoll.h>

int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout); int epoll_pwait(int epfd, struct epoll_event *events,
int maxevents, int timeout, const sigset_t *sigmask);
返回:发生事件数
epfd:由epoll_create生成的epoll专用的文件描述符
epoll_event:用于回传代处理事件的数组
maxevents:每次能处理的事件数
timeout:等待I/O事件发生的超时值

在linux中的man手册中有epoll模型:

            #define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd; /* Set up listening socket, 'listen_sock' (socket(),
bind(), listen()) */ epollfd = epoll_create();
if (epollfd == -) {
perror("epoll_create");
exit(EXIT_FAILURE);
} ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
} for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -); //等待IO事件
if (nfds == -) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
} for (n = ; n < nfds; ++n) {
//如果是主socket事件,则表示有新连接进入,需要进行新连接的处理
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &local, &addrlen);
if (conn_sock == -) {
perror("accept");
exit(EXIT_FAILURE);
}
//将新连接置于非阻塞模式
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
//注意这里的参数EPOLLIN|EPOLLET并没有设置对写socket的监听
//如果有写操作的话,这个时候epoll是不会返回事件的
//如果要对写操作也监听的话,应该是EPOLLIN|EPOLLOUT|EPOLLET
//并且将新连接也加入EPOLL的监听队列
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -) {
//加入到epoll的监听队列里,这里用EPOLL_CTL_ADD
//来加一个新的epoll事件,可以通过EPOLL_CTL_DEL减少
//一个epoll事件,通过EPOLL_CTL_MOD来改变一个i额事件的监听方式
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
//如果不是主socket的事件的话,则代表这是一个用户的socket事件
//则用来处理这个用户的socket的事情是,比如说read(fd, xxx)之类,或者一些其他的处理
do_use_fd(events[n].data.fd);
}
}
}

epoll模型

下面是个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>
#include <errno.h>
#include <stdlib.h>
#include <strings.h> using namespace std; #define MAXLINE 5
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 5000
#define INFTIM 1000 void setnonblocking(int sock)
{
int opts;
opts = fcntl(sock, F_GETFL);
if(opts < ) {
perror("fcntl(sock, GETFL)");
exit();
}
opts = opts | O_NONBLOCK;
if(fcntl(sock, F_SETFL, opts) < ) {
perror("fcntl(sock, SETFL, opts)");
exit();
}
} int main()
{
int i, maxi, listenfd, connfd, sockfd, epfd, nfds;
ssize_t n;
char line[MAXLINE];
socklen_t clilen; struct epoll_event ev, events[];
epfd = epoll_create();
struct sockaddr_in clientaddr;
struct sockaddr_in serveraddr;
listenfd = socket(AF_INET, SOCK_STREAM, ); setnonblocking(listenfd);
ev.data.fd = listenfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
char *local_addr = "192.168.1.63";
inet_aton(local_addr, &(serveraddr.sin_addr));
serveraddr.sin_port = htons(SERV_PORT);
bind(listenfd, (sockaddr *)&serveraddr, sizeof(serveraddr));
listen(listenfd, LISTENQ); maxi = ;
for( ; ; ) {
nfds = epoll_wait(epfd, events, , );
for(i=;i<nfds;i++) {
if(events[i].data.fd == listenfd) {
connfd = accept(listenfd, (sockaddr *)&clientaddr, &clilen);
if(connfd < ) {
perror("connfd < 0");
exit();
}
setnonblocking(connfd);
char *str = inet_ntoa(clientaddr.sin_addr);
std::cout<<"connect from"<<str<<std::endl;
ev.data.fd = connfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
} else if(events[i].events & EPOLLIN) {
if((sockfd = events[i].data.fd) < ) {
continue;
}
if((n = read(sockfd, line, MAXLINE)) < ) {
if(errno == ECONNRESET) {
close(sockfd);
events[i].data.fd = -;
} else {
std::cout<<"readline error"<<std::endl;
}
} else if(n == ) {
close(sockfd);
events[i].data.fd = -;
}
ev.data.fd = sockfd;
ev.events = EPOLLOUT | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
} else if(events[i].events & EPOLLOUT) {
sockfd = events[i].data.fd;
write(sockfd, line, n);
ev.data.fd = sockfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
}
}
}
}

epoll例子

最新文章

  1. spring 静态注入
  2. oracle、mysql新增字段,字段存在则不处理
  3. sql常见的面试题
  4. 编程Bug集
  5. json和string 之间的相互转换
  6. shell小细节
  7. Linux系统VIM编辑器
  8. 动态树LCT小结
  9. phantomjs 渲染
  10. shell练习--批量创建账号
  11. Java中布尔类型操作符&amp;=,|=与^=的使用
  12. 【转】 Android用于提示等待的ProgressDialog
  13. FACE++学习一、detect接口
  14. 深入浅出Node.js(一):什么是Node.js(转贴)
  15. UX是什么?
  16. 201521123048 《Java程序设计》第7周学习总结
  17. 基于 HTML5 WebGL 的 3D “弹力”布局
  18. java.util.HashMap和java.util.HashTable (JDK1.8)
  19. JBPM工作流(八)——流程实例(PI)Process Instance
  20. Redis 缓存应用实战

热门文章

  1. 【HDOJ6578】Blank(DP)
  2. IGServer for Java
  3. window安装nodejs
  4. 浅谈 STM32 硬件I2C的使用 (中断方式 无DMA 无最高优先级)(转)
  5. 异常的处理try-catch
  6. mybatis 插入
  7. 小程序报错 Please do not register multiple Pages in undefined.js
  8. 2018 China Collegiate Programming Contest Final (CCPC-Final 2018)(A B G I L)
  9. C# 生成小程序码
  10. 好1.1.4 PTA提交列表及说明