背景
通常来说,实现处理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 100
int 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;
}