seclect函数

  1. select函数

select函数主要是用来实现多路复用输入和输出模型,select系统调用是用来让我们监视多个文件句柄的状态变化的。程序会停在select处等待,直到被监视的文件句柄有一个或多个发生了状态。

select函数:

int select(int nfds,fd_set *readfds,fd_set writefds,fd_set  * exceptfds,struct timeval * timeout)

参数nfds是需要监视的最大的文件描述符值+1;
rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异
常文件描述符的集合。
struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件
发生则函数返回,返回值为0。
下面的宏提供了处理这三种描述词组的方式:
FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位
FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真
FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
FD_ZERO(fd_set *set);用来清除描述词组set的全部位
如果参数timeout设为:
NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了
事件。
0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。
函数返回值:
执行成功则返回文件描述词状态已改变的个数
如果返回0代表在描述词状态改变前已超过timeout时间,没有返回;
当有错误发生时则返回-1

有三种情况:

  1. 永远等待下去:仅在有一个描述符准备好I/O时才返回,为此,我们把该参数设为NULL.

  2. 等待固定时间:在有一个描述符准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微妙数。

  3. 根本不等待:检查描述符后立即返回,这称为轮询,为此该参数必须指向一个timeval结构,而且其中的定时器值必须为0;
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

const int fds_nums=64;
int fds_array[64];

static int startup(char* _ip,int _port)
{
	int sock=socket(AF_INET,SOCK_STREAM,0);
		if(sock<0)
		{
			perror("socket");
			exit(4);
		}
	int opt=1;
	//设置更加详细的socket信息
	setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
	struct sockaddr_in local;
	local.sin_family=AF_INET;
	local.sin_port=htons(_port);
	local.sin_addr.s_addr=inet_addr(_ip);

	if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
	{
		perror("bind");
		exit(5);
	}

	if(listen(sock,5)<0)
	{
		perror("listen");
		exit(6);
	}
	return sock;
}

int main(int argc,char* argv[])
{
	if(argc !=3)
	{
		printf("Usage:%s [ip] [port]\n",argv[0]);
		exit(1);
	}
     //对文件描述符数组进行初始化
	int i=0;
	for(;i<fds_nums;++i)
	{
		fds_array[i]=-1;
	}
	//创建监听套接字
	int listen_sock=startup(argv[1],atoi(argv[2]));
	//fd_set结构体仅包含一个整形数组,该数组的每个元素的每个比特位标记一个文件描述符
	//fd_set能够容纳的文件描述符数量由FD_SETSIZE指定,这就限制了select能够同时处理的文件描述符数量
	fd_set rset;
	FD_ZERO(&rset);//清除rset的所有位,即所有文件描述符
	FD_SET(listen_sock,&rset);//设置rset的listen_sock位

	int max_fd=-1;//最大的文件描述符的下标

	fds_array[0]=listen_sock;

	//设置select的延迟时间
    struct timeval timeout={5,0};//5秒 0微秒
	int done=0;
	while(!done)
	{
		max_fd=-1;
		i=0;
		for(;i<fds_nums;++i)
		{
			if(fds_array[i]>0)
			{
			FD_SET(fds_array[i],&rset);//	
			max_fd=max_fd>fds_array[i]?max_fd: fds_array[i];//计算max_fd
			}
		}
	
	// timeout.tv_sec=5;
	// timeout.tv_userc=0;
	// timeout={5,0}//每次都要重新设置
	// int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,
	//          struct timeval* timeval);
	//readfds、writefds、exceptfds分别指向可读、可写和异常事件对应的文件描述符集合进监听等待
	int ret=select(max_fd+1,&rset,NULL,NULL,NULL);//timeout为空表示阻塞等待
	
	switch(ret)
	{
		case 0:
		    printf("timeout...\n");//超时而且没有任何文件描述符就绪
			break;
		case -1:
		    perror("select");//出错
			exit(1);
		default:
			{
				i=0;
				for(;i<fds_nums;++i)
				{
		
					if(fds_array[i]==listen_sock && FD_ISSET(listen_sock,&rset))
					{
						//有可读的文件描述符就绪即迎宾的人已经就绪,可以让客人进来了
						//即可以连接客户端了accept
						struct sockaddr_in remote;
						socklen_t len=sizeof(remote);
						int new_sock=accept(listen_sock,(struct sockaddr*)&remote,&len);
						if(new_sock<0)
						{
							//连接失败
							perror("accept");
							exit(2);
						}
						printf("get a new client:socket ->%s:%d\n",inet_ntoa(remote.sin_addr),ntohs(remote.sin_port));
						//将可读的客户端加入rset
						int j=0;
						for(;i<fds_nums;++j)
						{
							if(fds_array[j]==-1)
							{
								fds_array[j]=new_sock;
								break;
							}

						}
						if(j==fds_nums)
						{
							//空间已用完,不能再添加新用户了
							close(new_sock);
						}
						
					}
					else
					{
						if(fds_array[i]>0&&FD_ISSET(fds_array[i],&rset))
						{
							char buf[1024];
							memset(buf,'\0',sizeof(buf));
							//从fds_array[i]这个客户端读取数据到buf
							ssize_t _s =read(fds_array[i],buf,sizeof(buf)-1);
							if(_s>0)
							{
								buf[_s-1]='\0';
								printf("client# %s\n",buf);

							}
							else if(_s==0)
							{
								printf("client close...\n");
								close(fds_array[i]);
								fds_array[i]=-1;
							}
							else
							{
								perror("read");
								exit(3);
							}
						}
					}
				}
				break;
			}
	   }
  }	
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值