epoll_socket

背景

通常来说,实现处理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: [email protected] 
    > Created Time: 2024年01月24日 星期三 16时33分07秒
 ************************************************************************/

#include
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;
}

你可能感兴趣的:(linux,复用io,epoll)