Linux中I/O复用——select函数详解及代码实现

本文详细解析了Linux系统中的I/O复用机制,重点介绍了select函数的工作原理及其使用。通过实例代码展示如何在实际应用中实现select函数,以提高程序的并发处理能力。内容涵盖文件描述符集合、超时时间设定、多路复用技术等关键点。
摘要由CSDN通过智能技术生成
Linux中I/O复用——select函数详解及代码实现

一、I/O复用

1、I/O复用概念:
 解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用。
2、I/O复用使用的场合:
(1)当客户处理多个描述符(通常是交互式输入、网络套接字)时,必须使用I/O复用。
(2)tcp服务器既要处理监听套接字,又要处理已连接套接字,一般要使用I/O复用。
(3)如果一个服务器既要处理tcp又要处理udp,一般要使用I/O复用。
(4)如果一个服务器要处理多个服务或多个服务时,一般要使用I/O复用。
3、I/O复用常用函数:select、poll、epoll


二、select函数
1、函数原型

int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *execptfds,struct timeval *timeout); 
功能:轮询扫描多个描述符中的任一描述符是否发生响应,或经过一段时间后唤醒。



2、函数的参数

参数名称说明
nfds指定要检测的描述符的范围所检测描述符最大值+1
readset
可读描述符集
监测该集合中的任意描述符是否有数据可读
writeset
可写描述符集
监测该集合中的任意描述符是否有数据可写
exceptset
异常描述符集
监测该集合中的任意描述符是否发生异常
timeout
超时时间
超过规定时间后唤醒
3、函数的返回值
(1)负值:select错误
(2)正值:某些文件可读写或出错
(3)0:等待超时,没有可读写或错误的文件


三、代码实现
1、服务器端代码select.c

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

#define MAX 10

void fds_init(int fds[]);
void fds_add(int fds[],int fd);
void fds_del(int fds[],int fd);

int create_socket();

int main()
{
	int sockfd = create_socket();
	assert(sockfd != -1);
	
	int fds[MAX];
	fds_init(fds);

	fds_add(fds,sockfd);

	fd_set fdset;

	while(1)
	{
		FD_ZERO(&fdset);
		int maxfd = -1;

		int i = 0;
		for(;i < MAX;i++)
		{
			if(fds[i] == -1)
			{
				continue;
			}
			
			FD_SET(fds[i],&fdset);
			if(maxfd < fds[i])
			{
				maxfd = fds[i];
			}
		}
		
		struct timeval tv = {5,0};
		
		int n = select(maxfd+1,&fdset,NULL,NULL,&tv);
		//printf("n = %d\n",n);
		if(n == -1)
		{
			perror("select error");
			continue;
		}
		else if(n == 0)
		{
			printf("time out\n");
			continue;
		}
		else
		{
			int i = 0;
			for(;i < MAX;i++)
			{
				if(fds[i] == -1)
				{
					continue;
				}
				
				if(FD_ISSET(fds[i],&fdset))
				{
					if(fds[i] == sockfd)
					{
						struct sockaddr_in caddr;
						int len = sizeof(caddr);
						int c = accept(sockfd,(struct sockaddr*)&caddr,&len);

						if(c < 0)
						{
							continue;
						}

						printf("accept c = %d\n",c);
						fds_add(fds,c);
					}
					else
					{
						char buff[128] = {0};
						if(recv(fds[i],buff,127,0) <= 0)
						{
							close(fds[i]);
							printf("one client over\n");
							fds_del(fds,fds[i]);
						}
						else
						{
							printf("read:%s\n",buff);
							send(fds[i],"OK",2,0);
						}
					}
				}
			}
		}
	}
}

int create_socket()
{
	 int sockfd = socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd != -1);
	    
	struct sockaddr_in saddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(6000);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
		     
	int res = bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));
	assert(res != -1);
	
	listen(sockfd,5);

	return sockfd;

}



void fds_init(int fds[])
{
	int i = 0;
	for(; i < MAX; i++)
	{
		fds[i] = -1;
	}
}

void fds_add(int fds[],int fd)
{
	int i = 0;
	for(; i < MAX;i++)
	{
		if(fds[i] == -1)
		{
			fds[i] = fd;
			break;
		}
	}
}

void fds_del(int fds[],int fd)
{
	int i = 0;
	for(; i < MAX ; i++)
	{
		if(fds[i] == fd)
		{
			fds[i] = -1;
			break;
		}
	}
}

2、客户端代码cli.c

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

int main()
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd != -1);

	struct sockaddr_in saddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(6000);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
	assert(res != -1);

	while(1)
	{
		printf("input :");
		char buff[128] = {0};
		fgets(buff,128,stdin);
		if(strncmp(buff,"end",3) == 0)
		{
			break;
		}
		
		send(sockfd,buff,strlen(buff),0);
		
		memset(buff,0,128);
		recv(sockfd,buff,127,0);
		printf("buff=%s\n",buff);
	}	
//	 close(sockfd);
}
运行结果:





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值