epoll的接口如下:
typedef union epoll_data{
void *ptr;
int fd;
__uint32_t u32;
_uint64_t u64;
}epoll_data_t;
struct epoll_event{
__uint32 events; //epoll events
epoll_data_t data; //user data variable
}
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);
EPOLLIN 表示对应的文件描述符可以读
EPOLLOUT 表示对应的文件描述符可以写
EPOLLPRI
EPOLLERR 表示对应的文件描述符发生错误
EPOLLHUP 表示对应的文件描述符被挂断
EPOLLET 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的
EPOLLONESHOT
Epoll的好处
- 相比于select,epoll 最大的好处在于他不随监听fd数目的增长而降低效率。
- 真正写服务器的时候,单机环境下能很明显的感觉到,epoll的效率高。具体体现在,多个client连接服务器的时候,select/poll不处理后连接的io事件,出现不响应的情况。
epoll 服务器实现框架
int main(int argc, char *argv[])
{
listenfd = socket();
bind();
listen();
struct epoll_event event; // 监听socket 对应的event
event.data.fd = listenfd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event); //添加监听socket event (key-value)
while (1)
{
nready = epoll_wait(epollfd, &*events.begin(), events.size(), -1);
for (int i = 0; i < nready; i++)
{
if (events[i].data.fd == listenfd) //处理新连接
{
//socket 4
connfd = accept();
activate_nonblock(connfd); //noblocking IO 和epoll 结合使用
epoll_event event;
event.data.fd = connfd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event); //添加连接socket (key-value)
}
else if (events[i].events & EPOLLIN) //连接socket io就绪
{
recv(sockfd, buff, nlength, 0); //读数据
send(sockfd, buff, nlength, 0); //返回数据
}
}
}
close(listenfd);
return 0;
}
epoll 服务器demo
/* epoll server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include <vector>
#include <sys/epoll.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <iostream>
#include <algorithm>
// #include "wrap.h"
#define MAXLINE 1024
#define SERV_PORT 2333
#define EVENTSIZE 20
void perr_exit(const char* s){
perror(s);
exit(1);
}
void activate_nonblock(int fd) {
int ret;
int flags = fcntl(fd, F_GETFL);
if(flags == -1)
perr_exit("fcntl");
flags |= O_NONBLOCK;
ret = fcntl(fd, F_SETFL, flags);
if(ret == -1)
perr_exit("fcntl");
}
int main(int argc, char *argv[])
{
int listenfd ,connfd;
char buff[MAXLINE];
char str[INET_ADDRSTRLEN]; /* #define INET_ADDRSTRLEN 16 */
int ret ;
int nready;
//socket 1
ret = listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(ret<0) perr_exit("socket");
struct sockaddr_in servaddr, cliaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
//socket 2
ret = bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(ret <0) perr_exit("bind");
printf("ret: %d Server IP: %s Port %d create_socket: %d\n",ret, inet_ntoa(servaddr.sin_addr),ntohs(servaddr.sin_port),listenfd);
//socket 3
ret = listen(listenfd,20);
if(ret <0) perr_exit("listen");
int nlength;
std::vector<int> clients;
std::vector<epoll_event> events(EVENTSIZE);
int epollfd = epoll_create1(EPOLL_CLOEXEC);
struct epoll_event event;
event.data.fd = listenfd;
event.events = EPOLLIN| EPOLLET;
epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&event);
while(1){
nready = epoll_wait(epollfd,&*events.begin(),events.size(),-1);
if (nready == -1)
{
if (errno == EINTR)
continue;
perr_exit("epoll_wait");
}
else if (0 == nready)
continue;
//
if ((size_t)nready == events.size())
events.resize(events.size() * 2);
for(int i=0;i < nready;i++){
if(events[i].data.fd == listenfd){
socklen_t cliaddr_len = sizeof(cliaddr);
//socket 4
if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len)) < 0)
perr_exit("accept");
printf("received from %s at PORT %d connfd %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port), connfd);
clients.push_back(connfd);
activate_nonblock(connfd); //noblocking IO
epoll_event event;
event.data.fd = connfd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&event); //key-value
}else if(events[i].events & EPOLLIN){
int sockfd = events[i].data.fd;
if(sockfd <0)
continue;
nlength = recv(sockfd,buff,sizeof(buff),0);
if(nlength <0) {
perr_exit("recv");
}
else if (nlength ==0)
{
close(sockfd);
epoll_event event = events[i];
epoll_ctl(epollfd,EPOLL_CTL_DEL,sockfd,&event);
// clients.erase(std::remove(clients.begin(),clients.end(),sockfd),clients.end());
clients.erase(std::remove(clients.begin(), clients.end(), sockfd), clients.end());
printf("client close ...\n");
}
for (int i = 0; i < nlength; i++)
buff[i] = toupper(buff[i]);
send(sockfd, buff, nlength, 0);
}
}
}
close(listenfd);
return 0;
}