浅谈select与epoll函数

本文详细介绍了select与epoll函数在并发编程场景中的使用方法及原理,包括设置读集、调用select等待数据、判断状态以及epoll函数的回调机制。通过对比select和epoll函数在处理大量客户端连接时的效率差异,阐述了epoll函数如何提高并发处理能力,减少重复劳动并提高处理速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一篇技术博客,哈哈。这里讲的内容都是很浅的,很多细节没讲到,比如select函数读集数量上限,超时,非堵塞设置等等,自己百度吧~~


应用场景:
很多个客户端同时访问服务器,服务器需要处理各个客户端消息。


用法:
select:
1. 设置读集:将一堆客户端fd通通装入readfds集合


FD_ZERO(&readfds); //先清空
for(i = 0; i < num_of_client; i++)
     FD_SET(&fd[i],&readfds);


2. 调用select函数等待数据,函数返回说明有数据,或者出错,或者超时


int err=select(maxfd+1,&readfds,NULL,NULL,NULL) ;


3. 根据select函数返回值判断什么情况


err = -1 出错

err = 0 超时
else 客户端有数据


4. 在上一步else的基础上用 FD_ISSET 逐个判断这堆客户端是否有数据


		for( i = 0; i< num_of_client; i++)
		if(FD_ISSET(fd[i],readfds)){ //第i号客户端有数据啦
			dosomething(i);
		}


最后一步可能有点莫名其妙,这里介绍下select下函数原理。
之前不是设置了一个读集 readfds 吗,把所有客户端fd都放进去了,然后调用select函数,系统就开始轮询这个读集里所有的fd。所以轮询周期取决于你放了几个fd到集合里,放越多效率自然就低。比如在某一次轮询结束后中,系统检测到有20个客户端有新数据过来,它就把这20客户端的fd做个标记。然后函数返回,返回值大于0(具体几我也没去看过)。


然后你需要做的就是把这20个客户端找出来。怎么找?看第4步。所以你自己也得手动轮询一遍,把那些被标记的客户端找出来,再处理消息。比如这个读集里有1000个客户端,然后这1000个客户端,你就需要用 FD_ISSET 每个都去问他一下,哎哥们是你有话要说吗?哥们说,不是,找错人了。而且不管这哥们是不是有话说,你都得把剩下的问完,一个不能拉下。


看到这里你是不是也觉得这个过程有些重复劳动?为什么系统在轮询的时候,在检测到某个客户端有数据的时候,不直接把这个客户端的fd拎出来放到一个地方存起来,还标记毛啊。这样我们可以直接得到那个地方放着的fd。毕竟1000个客户端只有20个有数据过来,问其他980个是不是有数据是浪费时间。

这个时候epoll函数出现了。形式有点不同,理解起来可以当一回事。当然内部实现不一样。
epoll函数就是只把那些有数据的客户端fd放到某个地方,而且函数返回值为有数据的客户端的数量。这样我们就省事多了。20个客户端有数据过来,函数返回20,然后我们只需要循环20次,就把每个消息给处理了。而且成功率百分百,妈妈再也不用担心我像之前那样吃980/1000个闭门羹了。


epoll函数内部实现不像select函数那样用轮询,轮询我也会,这么low。epoll用的是回调函数。这个我自己也没搞清楚怎么实现的,要继续研究。epoll就不一步步讲解了,用起来跟select差不多的。直接贴源码。

#include 	
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
/*
typedef union epoll_data { 
	void *ptr;
	int fd;
	__uint32_t u32;
	__uint64_t u64;
} epoll_data_t; 

struct epoll_event {
	__uint32_t events; // Epoll events 
	epoll_data_t data; // User data variable 
};
EPOLLIN		:表示对应的文件描述符可以读;
EPOLLOUT	:表示对应的文件描述符可以写;
EPOLLPRI	:表示对应的文件描述符有紧急的数据可读
EPOLLERR	:表示对应的文件描述符发生错误;
EPOLLHUP	:表示对应的文件描述符被挂断;
EPOLLET		:表示对应的文件描述符有事件发生;
*/
#define MAXNUM	1024
#define MAXBUFFISZE	1024

int main(int argc,char *argv[])
{
	int ret = 0;
	int i = 0;
	int connection_num = 0;
	int clientfd = -1;
	int readsize = 0;
	char buff[MAXBUFFISZE] = "";
	struct epoll_event new_connection;
	struct epoll_event events[MAXNUM];
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
		if(sockfd == -1){perror("socket");exit(EXIT_FAILURE);}
	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family=AF_INET; 
	servaddr.sin_port=htons(atoi(argv[2]));
	inet_aton(argv[1],(struct in_addr*)servaddr.sin_addr.s_addr);
	ret = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
		if(ret){perror("bind");exit(EXIT_FAILURE);}
	ret = listen(sockfd,20);
		if(ret){perror("listen");exit(EXIT_FAILURE);}
	struct sockaddr clientaddr;
	socklen_t addrlen=sizeof(clientaddr);

	int efd = epoll_create(MAXNUM);
		if(efd == -1){perror("epoll_create");exit(1);}
	new_connection.data.fd = sockfd;
	new_connection.events = EPOLLIN|EPOLLET;
	ret = epoll_ctl(efd, EPOLL_CTL_ADD, sockfd, &new_connection);
		if(ret == -1){perror("epoll_ctl");exit(1);}
	while(1){
		printf("waitting...\n");
		ret = epoll_wait(efd, events, MAXNUM, -1);
		if(ret == -1){perror("epoll_wait");break;}
		if(ret == 0){}
		for(i = 0;i < ret; i++){
			if(events[i].data.fd == sockfd){
				clientfd = accept(sockfd,&clientaddr,&addrlen);
				if(clientfd==-1){perror("accept");continue;}
				
				new_connection.data.fd = clientfd;
				new_connection.events = EPOLLIN|EPOLLET;
				ret = epoll_ctl(efd, EPOLL_CTL_ADD, clientfd, &new_connection);
				if(ret == -1){perror("epoll_ctl");continue;}
				printf("new connection:%d, total connection nums:%d\n", clientfd, ++connection_num);
			}else{
				clientfd = events[i].data.fd;
				memset(buff,0,MAXBUFFISZE);
				readsize = read(clientfd, buff,MAXBUFFISZE);	//设为非阻塞
				if(readsize < 0){
					printf("client %d has disconnect\n", clientfd);
					close(clientfd);
					ret = epoll_ctl(efd, EPOLL_CTL_DEL, clientfd, &new_connection);
					if(ret == -1){perror("epoll_ctl");}
					continue;
				}
				if(strcmp(buff, "end") == 0){
					printf("quitting...\n");
					close(clientfd);
					close(sockfd);
					return 0;
				}
				if(strcmp(buff, "done") == 0){
					close(clientfd);
					ret = epoll_ctl(efd, EPOLL_CTL_DEL, clientfd, &new_connection);
					if(ret == -1){perror("epoll_ctl");}
					printf("client %d done\n", clientfd);
					connection_num--;
					continue;										
				}
				printf("read %d bytes from client %d:%s\n", readsize, clientfd, buff);			
			}
			
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值