背景
通常来说,实现处理tcp请求,为一个连接一个线程,在高并发的场景,这种多线程模型与Epoll相比就显得相形见绌了。epoll是linux2.6内核的一个新的系统调用,epoll在设计之初,就是为了替代select, poll线性复杂度的模型,epoll的时间复杂度为O(1), 也就意味着,epoll在高并发场景,随着文件描述符的增长,有良好的可扩展性。
参考
epoll原理
epoll触发模式
对于触发模式重点,可以总结为下
如果对于一个非阻塞 socket,如果使用 epoll 边缘模式去检测数据是否可读,触发可读事件以后,一定要一次性把 socket 上的数据收取干净才行,也就是说一定要循环调用 recv 函数直到 recv 出错,错误码是EWOULDBLOCK(EAGAIN 一样)(此时表示 socket 上本次数据已经读完);如果使用水平模式,则不用,你可以根据业务一次性收取固定的字节数,或者收完为止
例子
/*************************************************************************> File Name: epollsvr.c> Author: ycj> Mail: 1484541288@qq.com > Created Time: 2024年01月24日 星期三 16时33分07秒************************************************************************/#include<stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>#define NAME_LEN 50
#define MAX_CLIENTS 100int size = 0;int init_server(const char* ip,unsigned short int port){int fd = socket(AF_INET,SOCK_STREAM,0);assert(fd != -1);int oldSocketFlag = fcntl(fd,F_GETFL,0);int newSocketFlag = oldSocketFlag | O_NONBLOCK;assert(fcntl(fd,F_SETFL,newSocketFlag) != -1);struct sockaddr_in addr = {};addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip);socklen_t addrlen = sizeof(addr);int ret = bind(fd,(const struct sockaddr*)&addr,addrlen);assert(ret != -1);ret = listen(fd,MAX_CLIENTS);assert(ret != -1);return fd;
}void accept_client(int fd,int epfd){struct sockaddr_in addr = {};socklen_t len = sizeof(addr);int cfd = accept(fd,(struct sockaddr*)&addr,&len);assert(cfd != -1);int oldSocketflag = fcntl(cfd,F_GETFL,0);int newSocketFlag = oldSocketflag | O_NONBLOCK;assert(fcntl(fd,F_SETFL,newSocketFlag) != -1);struct epoll_event event ={};event.events = EPOLLIN;event.data.fd = cfd;int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&event);assert(ret != -1);
}void select_fd(int fd){int epfd = epoll_create(MAX_CLIENTS);if(epfd == -1){perror("epoll_create");return;}struct epoll_event event = {};event.events = EPOLLIN;//event.events |= EPOLLET;event.data.fd = fd;int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&event);if(ret == -1){perror("epoll_ctl");return;}while(true){struct epoll_event events[MAX_CLIENTS+1] = {};int i;ret = epoll_wait(epfd,events,MAX_CLIENTS+1,-1);if(ret == -1){perror("epoll_wait");break;}for(i=0;i<ret;i++){if(events[i].events & EPOLLIN){if(events[i].data.fd == fd){accept_client(fd,epfd);}else{char msg[1024] = {};ret = recv(events[i].data.fd,msg,1024,0);if(ret <= 0){ret = epoll_ctl(epfd,EPOLL_CTL_DEL,events[i].data.fd,NULL);assert(ret != -1);}else{printf("msg : %s\n",msg);}}}else if(events[i].events & EPOLLOUT){if(events[i].data.fd != fd)printf("input msg...\n");}else{//....}}}
}int main(int argc,char *argv[]){if(argc < 3){printf("%s ip port\n",argv[0]);return -1;}int fd = init_server(argv[1],atoi(argv[2]));select_fd(fd);close(fd);return 0;
}