一、
原理:先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。
函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。
int select(int maxfd, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *timeout);
参数:
maxfd
所有监控的文件描述符中最大的那一个加1
read_fds
所有要读的文件文件描述符的集合
write_fds
所有要的写文件文件描述符的集合
except_fds
其他要向我们通知的文件描述符
timeout
超时设置.
Null:一直阻塞,直到有文件描述符就绪或出错
时间值为0:仅仅检测文件描述符集的状态,然后立即返回
时间值不为0:在指定时间内,如果没有事件发生,则超时返回。
在我们调用select时进程会一直阻塞直到以下的一种情况发生.
有文件可以读.
有文件可以写.
超时所设置的时间到.
设置文件描述符我们要使用几个宏:
void FD_ZERO(fd_set *fdset) 将fd加入到fdset
void FD_SET(int fd,fd_set *fdset) 将fd从fdset里面清除
void FD_CLR(int fd,fd_set *fdset) 从fdset中清除所有的文件描述符
int FD_ISSET(int fd,fd_set *fdset) 判断fd是否在fdset集合中
select( )函数里面的各个文件描述符fd_set集合的参数在select( )前后发生了变化:
前:表示关心的文件描述符集合
后:有数据的集合(如不是在超时还回情况下)
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <errno.h>
#define PORT 5005
#define N 15
#define MAXSIZE 32
int main(int argc, const char *argv[])
{
int fd;
int i;
if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0){
perror("socket");
exit(1);
}
int reuse;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int));
struct sockaddr_in sin;
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0){//绑定
perror("bind");
exit(1);
}
listen(fd,5);//监听
int newfd = -1;
int fd_all[N]; //数组用于添加文件描述符,判断select之后有数据的描述符
for(i=0;i<N;i++)
fd_all[i] = -1;
struct sockaddr_in cin;
socklen_t len = sizeof(cin);
fd_set rset;
FD_SET(fd,&rset);
fd_set select_t; //创建两个fd_set集合,一个用于select,一个用于数据备份,方便下面重新添加描述符
fd_all[0] = fd; //数组第一个数据是监听的描述符
char buf[32];
int ret;
struct timeval tout;
int maxfd = -1;
maxfd = fd;
while(1)
{
FD_ZERO(&select_t);
select_t = rset;
tout.tv_sec = 2;
tout.tv_usec = 0;
select(maxfd + 1,&select_t,NULL,NULL,&tout);
if(FD_ISSET(fd,&select_t)) //若是监听的描述符,执行accept
{
if((newfd = accept(fd,(struct sockaddr *)&cin,&len)) < 0){
perror("accept");
exit(1);
}
printf("client%d is connecting!\n",newfd);
FD_SET(newfd,&rset);
for(i=0;i<N;i++) //把新连接的描述符添加到数组中
if(fd_all[i] != -1)
continue;
else{
fd_all[i] = newfd;
break;
}
if(i == 15){
printf("用户数量已达最大\n");
continue;
}
if(newfd > maxfd) //更新最大的文件描述符
maxfd = newfd;
}else{ //否则就是有数据的描述符,
for(i=1;i<N;i++){
if(FD_ISSET(fd_all[i],&select_t)){ //用循环判断有数据的fd;
bzero(buf,sizeof(buf));
do{
ret = read(fd_all[i],buf,MAXSIZE);
}while(ret < 0 && errno == EINTR);
if(ret < 0){
perror("read");
continue;
}
if(ret == 0){
printf("client%d is exit!\n",fd_all[i]);
FD_CLR(fd_all[i],&rset); //在用户退出之后,把描述符从集合中删除,同时关闭描述符将数组相应位置重置
close(fd_all[i]);
fd_all[i] = -1;
continue;
}
printf("client %d: %s",fd_all[i],buf);
}
}
}
}
return 0;
}