1.epoll原理
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
优点:
1.支持一个进程打开大数目的socket描述符(FD)
2.IO效率不随FD数目增加而线性下降
3.使用mmap加速内核与用户空间的消息传递
4.内核微调
2.epoll基本操作
-
在监听之后用epoll_create( max)函数创建epoll,返回值为标识符epfd;其中max为epoll所支持的最大柄数;在用完后记得用close()来关闭这个句柄
-
创建struct epoll_event 类型的ev和events[MAX_EVENTS];
ev.events = EPOLLIN(可读);ev.data.fd = sockfd; -
在循环里面,用epoll_wait(int epfd, epoll_event events, int maxevents, int timeout)来查询所有网络接口,看哪个可读/可写,
events是一个epoll_events*指针,当epoll_wait函数操作成功后,epoll_events里面将存储所有的读写事件,最后一个参数是epoll_wait超时的话,参数为0马上返回,参数为-1表示一直等下去。 -
epoll_wait之后是一个循环,遍历所有事件;
常用框架:
for (;;)
{
nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (nfds == -1)
{
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n)
{
if (events[n].data.fd == listenfd) //有新的连接
{
connfd = accept(listenfd,(struct sockaddr *) &clientaddr, &clilen); //accept这个连接
if (confd == -1)
{
perror("accept");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = connfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd,&ev) //将新fd添加到epoll监听队列中
else if(events[i].events & EPOLLIN) //接收到可读数据,读socket
{
ret = recv(events[i].data.fd, buf, sizeof(buf), 0);
if(-1 == ret)
{
perror("recv");
exit(1);
}
else if(0 == ret)
{
ev.data.fd = events[i].data.fd;
ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &ev); //客户端退出, 注销事件
close(events[i].data.fd);
}
else
{
printf("收到%d客户端的消息%s\n", events[i].data.fd, buf);
}
}
else if(events[i].events & EPOLLOUT) //接收到可写数据,写socket
{
....
}
}
}
}
具体代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#define MAXSIZE 256
int main()
{
//创建socket
int sockfd = socket(AF_INET, SOCK_STREAM,