-
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将超时返回。
函数返回值:
执行成功则返回文件描述词状态已改变的个数
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
有三种情况:
-
永远等待下去:仅在有一个描述符准备好I/O时才返回,为此,我们把该参数设为NULL.
-
等待固定时间:在有一个描述符准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微妙数。
-
根本不等待:检查描述符后立即返回,这称为轮询,为此该参数必须指向一个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;
}
}
}
}