select用法

一、
原理:先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个已准备好进行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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值