网络编程_select、poll、epoll

本文介绍了套接字编程中select、poll和epoll的区别和使用。select方法监听文件描述符的位数组,poll使用结构体数组,而epoll通过红黑树高效管理。epoll_wait只检查已准备就绪的事件,效率更高。文章通过示例代码展示了三者的工作原理,并指出epoll在高并发场景下的优势。

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

select:
类似poll,只是select监听的是文件描述符的位数组。监听的数组每一位与真正的文件描述符是一一对应的。

server.c

#include<stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>						
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>	

	void start(){
	//֨ӥһٶޡٹͥèθçַ֘ޡٹͥé׷Ҫ՚listenfdݨbքʱ۲ԃ
	struct sockaddr_in my_addr; 
	my_addr.sin_family = AF_INET;				// ipv4
	my_addr.sin_port   = htons(8080);
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//
	//֨ӥһٶޡٹͥèθçַ֘ޡٹͥé׷Ҫ՚acceptքʱ۲ԫԶࠍۧ׋քхϢ
	struct sockaddr_in client_addr;		   
	char cli_ip[INET_ADDRSTRLEN] = "";	   
	//fdìԫࠍۧ׋ͨх
	int clientfd = 0;
	
	//փսһٶlistenքfd
	int listenfd = socket(AF_INET,SOCK_STREAM,0);
	//ѳ֨һٶ׋ࠚ+ip,̹ӔϒćࠉӔɏΪһٶsocketߍˇһٶip+׋ࠚքθç޸Ԍܲ֟τݾ
	bind(listenfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
	//ʨ׃ͬʱŜٻޓ˜ȫȳΪ128ٶìעӢˇͬһʱݤ
	listen(listenfd, 128);
	printf("listen client @port=%d...\n",8080);
	//փսһٶfd
	//connfd = accept(lfd, (struct sockaddr*)&client_addr, &cliaddr_len);
	//inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
	//printf("----------------------------------------------\n");
	//printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));
	//ӎ˽֨ӥ
	
	int lastfd = listenfd;
	int i;
	//λͼìڃȒmޢΪܯۏìŜٻݠͽսR˂ݾ
	fd_set readset,totalSet;
	//ЈȥࠕёλͼքֵּلΪ0
	FD_ZERO(&readset);
	//listenfd ҭʾlistenfdϒѨҪݠͽ̻քR˂ݾ
	FD_SET(listenfd, &totalSet);
	
	while(1)
		{
			readset = totalSet;
			//setup 1 ёݠͽք͗ޓؖۍࠍۧ׋ք͗ޓؖٸselect
			int z = select(lastfd+1,&readset,NULL,NULL,NULL);
			//Ɛ׏ˇرԐ˂ݾעʺ
			if(z>0){
				//ɧڻԐ˂ݾעʺìǤʵϒҢһ֪րˇʲô˂ݾìիˇԐ}זȩ࠶
				if(FD_ISSET(listenfd,&readset)){
					socklen_t cliaddr_len = sizeof(client_addr); //ӲΪ̻ˇԫɫԫԶìܡزه
					clientfd = accept(listenfd, (struct sockaddr*)&client_addr, &cliaddr_len);
					inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
					printf("----------------------------------------------\n");
					printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));
					FD_SET(clientfd, &totalSet);	
					lastfd=clientfd;
					if(0==--z){
						continue;
					}
				}
				
				for(i=listenfd+1;i<=lastfd;i++){//Ԧmࠍۧ׋עڽ4քлϢ
					if(FD_ISSET(i,&readset)){//ɧڻ֚IٶfdԐR˂ݾìŇôֱޓёŚɝׁԶ4
						char recv_buf[512] = "";
						int rs=read(i,recv_buf,sizeof(recv_buf));
						if(rs==0){//ࠍۧ׋ژҕ
							close(i);
							FD_CLR(i,&totalSet);
						}else{
							printf("%s\n",recv_buf);
							//write(0,recv_buf,rs);
						}
						
					}
					
				}
				
			}
			
		}
}

int main(){
	start();
	return 0;
}

poll:

首先,服务启动时,服务端的socket会server_init()返回值int s 是一个文件描述符,将这个文件描述符放到一个pollfd数组(或链表)pfd的第0位中,

while(1){

int ret 	poll(pfd , 监听的文件描述符个数 , ...)

ret <0,有error;ret = 0,超时;ret>0 ,有事件发生。

然后:

if(pfd[0].revents & POLLIN){
	是连接事件
	int rws = accept(s,...);
	if(rws < 0 ){
		error;
	}

	并将这个rws放入到pfd数组中

}

最后,遍历数组pfd的从1开始的文件描述符
if(pfd[i].revents & POLLIN){
	有写事件发生
	int num =	recv(pfd[i].fd ,buf ,...)
	num 为读到数据的字节数。
}

}

PS:accept只监听连接文件描述符。select方法可以监听连接或读写事件的发生。recv只能监听读写事件的发生。

epoll:

将待监控的文件描述符存放在一个树中,调用epoll_wait(),监控这些文件描述符,当这些文件描述符状态有变化时,会将文件描述符与具体的状态绑定一起,只需要取出后进行判断就可以了。

在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个rdllist双向链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个rdllist双向链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。

socket可以理解为: ip + 端口 形成的一个文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值