首页 > 其他分享 >select函数详解:IO复用

select函数详解:IO复用

时间:2024-09-10 20:51:56浏览次数:3  
标签:文件 set 函数 int 复用 描述符 IO select

select函数概述

select函数是一种用于实现I/O复用的方法,它可以让程序在多个文件描述符(例如套接字)之间进行选择,以便在其中任何一个或多个可用时执行I/O操作。这种机制使得程序能够更高效地处理多个I/O操作。下面将对select的原理和工作机制进行详细介绍,并分析select函数的优势和局限。

1. select的原理和工作机制

select函数的原理和工作机制可以概括为以下几个步骤:

  1. 初始化文件描述符集合:程序需要为select函数准备三个文件描述符集合,分别表示要监控的读、写和异常条件。这些集合通常由FD_SET、FD_CLR、FD_ISSET和FD_ZERO这四个宏来操作。

  2. 调用select函数:程序调用select函数,并传入监控的文件描述符集合。此外,还需要设置一个超时时间,以便在没有任何I/O事件发生时,select函数能够在超时后返回。

  3. 等待I/O事件:select函数会阻塞,直到至少有一个文件描述符准备好进行I/O操作,或者超时时间到达。

  4. 检查文件描述符状态:select函数返回后,程序需要检查文件描述符集合的状态,以确定哪些文件描述符准备好进行I/O操作。然后,程序可以根据文件描述符的状态来执行相应的读、写或异常处理操作。

  5. 重复以上过程:在执行完当前的I/O操作后,程序可以再次调用select函数,以继续监控文件描述符的状态。

2.select函数详解

为了更好地理解select函数,本节将详细介绍其函数原型、参数解析以及返回值分析。
2.1. 函数原型
在C语言中,select函数的原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
2.2. 参数解析
接下来,我们将逐个解析select函数的参数。
2.2.1. nfds
nfds表示需要监控的文件描述符的最大值加1。这个值通常设为所有文件描述符中的最大值加1,以确保select能够正确地监控所有需要的文件描述符。
2.2.2. readfds, writefds, exceptfds
readfds,writefds和exceptfds分别表示要监控的读、写和异常条件的文件描述符集合。它们是由fd_set类型表示的位图结构。可以使用以下四个宏来操作这些集合:
• FD_SET(fd, &set): 将文件描述符fd添加到set集合中。
• FD_CLR(fd, &set): 从set集合中删除文件描述符fd。
• FD_ISSET(fd, &set): 检查fd是否在set集合中。
• FD_ZERO(&set): 清空set集合。
2.2.3. timeout
timeout参数是一个timeval结构指针,用于设置select函数的超时时间。当timeout为NULL时,select将无限期地等待,直到有文件描述符准备好。当timeout设置为0时,select将立即返回。当timeout设置为非零值时,select将等待指定的时间,直到有文件描述符准备好或超时。
timeval结构如下:
struct timeval {
    long tv_sec;   // seconds
    long tv_usec;  // microseconds
};

2.3. 返回值分析

select函数的返回值表示以下三种情况:
1. 返回值大于0:表示有准备好的文件描述符,即已经发生的I/O事件数量。
2. 返回值等于0:表示超时,即在指定的时间内没有任何I/O事件发生。
3. 返回值小于0:表示发生错误。在这种情况下,可以使用perror或strerror函数来获取错误信息。
在调用select函数后,可以通过检查readfds,writefds和exceptfds集合的状态,以确定哪些文件描述符准备好进行I/O操作。然后,程序可以根据文件描述符的状态来执行相应的读、写或异常处理操作。

3.使用IO多路复用完成TCP并发服务器

#include <myhead.h>
#define SERIP "192.168.0.135"
#define SERPORT 8888
#define BACKLOG 10
int main(int argc, const char *argv[])
{
	//1创建套接字
	int oldfd = socket(AF_INET,SOCK_STREAM,0);
	if(oldfd==-1)
	{
		perror("socket");
		return -1;
	}

	//2启动端口号快速复用
	int kkk=9;
	if(setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&kkk,sizeof(kkk))==-1)
	{
		perror("setsockopt");
		return -1;
	}
	printf("端口号快速复用成功\n");
	//定义要绑定的服务器Ip和端口号
	struct sockaddr_in server = {
	.sin_family = AF_INET,
	.sin_port = htons(SERPORT),
	.sin_addr.s_addr = inet_addr(SERIP)
	};
	//3绑定IP和端口号
	if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1)
	{
		perror("bind");
		return -1;
	}

	//4监听
	if(listen(oldfd,BACKLOG)==-1)
	{
		perror("listen");
		return -1;
	}
	printf("监听成功\n");
	struct sockaddr_in client;
	int client_len = sizeof(client);
	char buff[1024];

	fd_set readfds,temp;//1、定义容器存储描述符
	FD_ZERO(&readfds);//2、清空容器
	FD_SET(0,&readfds);//3放入输入描述符
	FD_SET(oldfd,&readfds);//4、放入台阶在描述符

	int maxfd = oldfd;//定义最文件描述符
	int newfd;

	while(1)
	{
		temp = readfds;
		int res = select(maxfd+1,&temp,NULL,NULL,NULL);//永久阻塞
		if(res==0)
		{
			printf("timeout");
			return -1;
		}
		if(res==-1)
		{
			perror("select");
			return -1;
		}
		//程序执行至此说明描述符有事件触发了select函数
		for(int i = 0;i<=maxfd;i++)//遍历所有的文件描述符
		{
			
			if(!FD_ISSET(i,&temp))//描述符不存在集合内继续下次循环
			{
				continue;
			}

			if(i==oldfd)//套接字事件解除的阻塞
			{
				//5接收客户端请求
				newfd = accept(oldfd,(struct sockaddr *)&client,&client_len);
				if(newfd==-1)
				{
					perror("accept");
					return -1;
				}
				printf("客户端%s:%d已上线\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
				FD_SET(newfd,&readfds);//新客户端产生后将新描述符放入集合
				maxfd = newfd>maxfd?newfd:maxfd;//更新最大文件描述符
			}
			else//newfd产生的解除阻塞
			{
				bzero(buff,sizeof(buff));//清空数组
				int len = recv(newfd,buff,sizeof(buff),0);//接收消息
				if(len==0)
				{
					printf("客户端下线\n");
					FD_CLR(i,&readfds);
					maxfd = newfd>maxfd?newfd:maxfd;//再次更新最大文件描述符
				}
				printf("收到信息:%s\n",buff);
				strcat(buff,"*_*");
				send(newfd,buff,sizeof(buff),0);//回复客户端消息
				printf("回复成功\n");	
			}
		
		}
	}
	close(oldfd);
	return 0;
}

标签:文件,set,函数,int,复用,描述符,IO,select
From: https://blog.csdn.net/m0_58572142/article/details/141953597

相关文章

  • visualstudio 工具箱如何批量加载devexpress控件?
     1.DevExpress简单介绍        DevExpress是一套功能强大的‌.NET用户界面控件开发套包,广泛应用于企业内容管理、成本管控、进程监督、生产调度等领域。以下是DevExpress在不同平台和控件中的使用方法:DevExpressWinForms控件的使用方法‌TreeList控件‌:设置Dock属......
  • ECOM 2001 Term Project Description
    ECOM 2001 TermProjectDescriptionDue 30Septemberat 9:00AMAWSTIntroductionThe aim of thisproject is toprepare, evaluate and analyse stockmarket data and torecommend an optimalportfo- lioconsistingof two stocks. Youhavebeen......
  • 【Azure Service Bus】创建 ServiceBus 的Terraform脚本报错GetAuthorizationRule: In
    问题描述在使用Terraform部署ServiceBus时候,遇见了如下报错:Error:ErrormakingReadrequestonAzureServiceBusTopicAuthorizationRule:servicebus.TopicsClient#GetAuthorizationRule:Invalidinput:autorest/validation:validationfailed:parameter=authorization......
  • 【五一省选集训day4】Permutation
    【五一省选集训day4】Permutation每次操作把数分成两组,每组内的顺序不变,把第\(0\)组放到第\(1\)组前面。发现这很像基于二进制的基数排序。假设我们进行\(k\)次这样的操作,就相当于给每个数赋一个值\((x,y)\),其中\(0\lex\le2^k-1,y=\texttt{数的下标}\)。然后对第一维......
  • 什么是TIOBE?
    TIOBE是编程语言排行榜(theimportanceofbeingearnest”的缩写),会定期的发布编程语言的排名列表。TIOBE通过计算搜索引擎的搜索结果来对编程语言的流行度进行评估,当然,在评估的过程中会屏蔽掉与编程语言无关或者泛化的结果,以此来提高评估结果的精确度。但是TIOBE也具有一定的局限......
  • JAVA —— IO流续
    复习一下上次的字节流 一、字符流1.Reader——FileReader 文件字符输入流——读字符数据进来2.Writer——FileWriter文件字符输出流——写字符数据进去  1.Reader——FileReader(文件字符输入流) publicstaticvoidmain(String[]args){ try( ......
  • IO多路复用、服务器模型
    IO多路复用:epollepoll的提出--》它所支持的文件描述符上限是系统可以最大打开的文件的数目;eg:1GB机器上,这个上限10万个左右。每个fd上面有callback(回调函数)函数,只有产生事件的fd才有主动调用callback,不需要轮询。注意:Epoll处理高并发,百万级1.红黑树:是特殊的二叉树......
  • 线段树 transformation——hdu 4578
    问题描述:给定一个数列,数列中所有元素都初始化为0,对其执行多种区间操作操作1:add修改:对区间[L,R]内的所有数加c操作2:multi修改:对区间[L,R]内所有数乘以c操作3:change操作:把区间[L,R]内所有数改为c操作4:sum操作:对区间中的每个数的p次方求和。1<=p<=3输入:有不超过10个测试用例。......
  • 【Azure Service Bus】创建 ServiceBus 的Terraform脚本报错GetAuthorizationRule: In
    问题描述在使用Terraform部署ServiceBus时候,遇见了如下报错:Error:ErrormakingReadrequestonAzureServiceBusTopicAuthorizationRule:servicebus.TopicsClient#GetAuthorizationRule:Invalidinput:autorest/validation:validationfailed:parameter=authorizat......
  • 【五一省选集训day4】Mansion
    【五一省选集训day4】Mansion注意,本题要求输出最大值,不要把最大值看成编号……srds好像只有我看错了。这个东西一看就很能用莫队做。用莫队按\(l\)分块,再按\(r\)排序。维护一棵线段树,每次移动对线段树进行单点修改和区间求\(\max\),一共\(n\sqrt{n}\)次移动,总时间复杂度......