epoll 是Linux 2.6内核提出的 ,可以理解其为select和poll的增强版
优点:
支持一个进程打开大数目的socket描述符(FD)
IO效率不随FD数目增加而线性下降
epoll还维护了一个双链表,用户存储发生的事件
一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题
实现epoll逻辑
1、创建epoll实例:epoll_create()
2、修改epoll的兴趣列表:epoll_ctl()
3、事件等待:epoll_wait()
实例
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_LISTEN_QUE 13
#define MAXEVENTSIZE 512
#define BUFF "hello client , i'm server!"
//初始化并返回套接字函数,socket、bind、listen
int socket_init(void)
{
int ser_fd = -1;
int rv = -1;
int on = 1;
int port = 9998;
struct sockaddr_in ser_addr;
ser_fd = socket(AF_INET, SOCK_STREAM, 0);
if(ser_fd < 0)
{
printf("create socket failure:%s\n", strerror(errno));
return -1;
}
printf("\ncreate socket[%d]successfully!\n",ser_fd);
if((rv = setsockopt(ser_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)
{
printf("\n设置地址重用失败:%s\n",strerror(errno));
return -1;
}
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(port);
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
rv = bind(ser_fd, (struct sockaddr *)&ser_addr, sizeof(ser_addr));
if(rv < 0)
{
printf("ser_fd[%d]bind port[%d]failure:%s\n", ser_fd, port, strerror(errno));
return -1;
}
printf("\nser_fd[%d]bind port[%d]successfully!\n", ser_fd, port);
rv = listen(ser_fd, MAX_LISTEN_QUE);
if(rv < 0)
{
printf("\nser_fd[%dlisten port[%d]failure:%s\n", ser_fd, port, strerror(errno));
return -1;
}
printf("\nser_fd[%dlisten port[%d]successfully!\n", ser_fd, port);
return ser_fd;
}
int main(int argc, char **argv)
{
int listenfd = socket_init();
int epollfd;
int rv,num,connfd;
struct epoll_event event;
struct epoll_event events_array[MAXEVENTSIZE];
struct sockaddr_in cliaddr;
char buff[1024] = {};
socklen_t len;
epollfd = epoll_create(111);//创建一个新的epoll实例(描述符),其对应的兴趣列表初始化为空。
if((epollfd = epoll_create(111)) < 0)
{
printf("epoll_create failure:%s\n", strerror(errno));
return -1;
}
printf("epoll_create [%d] successfully!\n", epollfd);
event.events = EPOLLIN;
event.data.fd = listenfd;
rv = epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);
if(rv < 0)
{
printf("epoll_trl failure:%s\n", strerror(errno));
close(epollfd);
return -1;
}
while(1)
{
printf("epoll waiting····\n");
num = epoll_wait(epollfd, events_array, MAXEVENTSIZE, -1);
if(num < 0)
{
printf("epoll_wait failure:%s\n", strerror(errno));
close(epollfd);
return -1;
}
else if(num ==0)
{
printf("eopll_wait timeout!\n");
close(epollfd);
continue;
}
printf("epoll_wait successfully and return %d \n", num);
for(int i=0; i<num; i++)
{
if(events_array[i].events == EPOLLIN && events_array[i].data.fd == listenfd)
{
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
if(connfd < 0)
{
printf("accpet new client failure:%s\n", strerror(errno));
continue;
}
printf("accpet new client[%d] successfully\n", connfd);
//将新连接进来的客户端注册到epollfd中
event.events = EPOLLIN;
event.data.fd = connfd;
rv = epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);
if(rv == 0)
{
printf("add new connfd[%d] to epollfd successfully!\n", connfd);
}
else if(rv < 0)
{
printf("add new connfd[%d] to epollfd failure:%s\n", connfd, strerror(errno));
close(connfd);
continue;
}
}
else
{
memset(buff, 0, sizeof(buff));
rv = read(events_array[i].data.fd, buff, sizeof(buff));
if(rv < 0)
{
printf("read data from client[%d]failure:%s\n", events_array[i].data.fd, strerror(errno));
close(events_array[i].data.fd);
continue;
}
else if(rv == 0)
{
printf("client[%d] get disconnetion!\n", events_array[i].data.fd);
close(events_array[i].data.fd);
continue;
}
printf("read data from client[%d]successfully:%s\n", events_array[i].data.fd, buff);
rv = write(events_array[i].data.fd, BUFF, strlen(BUFF));
if(rv < 0)
{
printf("send data to client[%d]failure:%s\n", events_array[i].data.fd, strerror(errno));
close(events_array[i].data.fd);
continue;
}
printf("send data to client[%d]successfully\n", events_array[i].data.fd);
}
}
}
close(listenfd);
}