1.介绍
epoll和之前介绍的select/poll有很大的差异,几乎现在所有的高并发I/O模型都使用epoll(如nginx)。
#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
接下来逐一介绍这3个API:
(1)epoll_create 创建一个epoll
int epoll_create(int size);
参数:
- size 在Linux 2.6.8版本后没有实际意义,但是必须填大于0的数。成功返回非负值,错误返回-1。
(2)epoll_ctl 设置创建的epoll
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数:
- epfd epoll_create的返回值
- op 对一个fd,在epoll的操作类型:EPOLL_CTL_ADD(添加)、 EPOLL_CTL_MOD(修改)、 EPOLL_CTL_DEL(删除)
- fd 要操作的fd
- event
struct epoll_event的结构体描述如下:
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
epoll_data_t定义如下:
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
epoll_ctl 成功返回0,错误返回-1。
(3)epoll_wait 检测事件。
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
参数:
- epfd 上文中的epollfd
- events 输出参数,存放事件信息的数组
- maxevents 数组的个数
- timeout 超时,单位为毫秒
epoll_wait 成功会返回事件的 fd 数目;返回 0 表示超时;失败返回 -1。
2.编码实战
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/epoll.h>
int main(int argc, char* argv[]){
int fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0){
printf("create socket failed!, errno=%d\n",errno);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(8080);
int ret;
ret = bind(fd, (struct sockaddr*) &addr, sizeof(addr));
if(ret == -1){
printf("bind failed!, errno=%d",errno);
close(fd);
}
ret = listen(fd, SOMAXCONN);
if(ret == -1){
printf("listen failed!, errno=%d",errno);
}
struct sockaddr_in clientaddr;
socklen_t clientaddrlen = sizeof(clientaddr);
//创建epoll
int epollfd = epoll_create(1);
if(epollfd == -1){
printf("create epoll failed!\n");
close(fd);
return -1;
}
struct epoll_event listenfd;
listenfd.data.fd = fd;
listenfd.events = EPOLLIN;
//设置socket(fd添加到epollfd)
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &listenfd);
if(ret == -1){
printf("epoll_ctl epoll failed!\n");
close(fd);
return -1;
}
//开始用epoll来管理多个连接
while(true){
epoll_event afds[256];//处理256个连接
int num = epoll_wait(epollfd, afds, 256, 1000);//监听可读事件
printf("epoll n=%d!\n",num);
if(num < 0){
printf("epoll error!\n");
break;
}
else if(num == 0){
printf("timeout!\n");
continue;//超时
}
else{
for(int i = 0; i < num; ++i)
{
if(afds[i].events == EPOLLIN)
{
if(afds[i].data.fd == fd)
{ //客户端来的连接
int afd = accept(fd, (struct sockaddr*) &clientaddr, &clientaddrlen);
printf("accept afd= %d\n",afd);
if(afd == -1)
{
break;
}
printf("A client connected!,ip=%s,port=%d\n",inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
struct epoll_event tmp_fd;
tmp_fd.data.fd = afd;
tmp_fd.events = EPOLLIN;
int ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, afd, &tmp_fd);
if(ret == -1)
{
printf("epoll_ctl epoll failed!\n");
close(fd);
return -1;
}
}
else{
printf("afds[%d]: fd=%d,events=%d\n",i,afds[i].data.fd,afds[i].events);
char buf[256] = {0};
int len = recv(afds[i].data.fd, buf, 256, 0);
if(len <= 0){
printf("afd[%d] close or error\n",i);
close(afds[i].data.fd);
afds[i].data.fd = -1;
}
printf("afd[%d] recv %d bytes:%s\n", i, len, buf);
}
}
}
}
}
close(fd);//关闭监听socket
}