select

作用:

网络编程中的select是用来让我们的程序监视多个文件描述符的状态变化的。程序会停在select这里等待,直到被监视的文件描述符有某一个或多个发生了状态改变。而sokcet就是一个文件描述符。

  在网络编程中,利用select来遍历所有socket的状态,包括服务端的socket,来实现非阻塞监听,和非阻塞接收。


函数参数解析:

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 

函数参数说明:

ndfsselect监视的文件句柄数,视进程中打开的文件数而定,一般设为要监视各文件中的最大文件描述符值加1。
readfds这个文件描述符集合监视文件集中的任何文件是否有数据可读,当select函数返回的时候,readfds将清除其中不可读的文件描述符,只留下可读的文件描述符。
 select()函数准备好读的条件:
    1>.套接口有数据可读
    2>.该连接的读这一半关闭(也就是接收了FIN的TCP连接)。对这样的套接口进行读操作将不阻塞并返回0(也就是返回EOF)。
    3>.该套接口是一个侦听套接口且已完成的连接数不为0。
    4>.其上有一个套接口错误待处理,对这样的套接口的读操作将不阻塞并返回-1,并设置errno。
writefds这个文件描述符集合监视文件集中的任何文件是否有数据可写,当select函数返回的时候,writefds将清除其中不可写的文件描述符,只留下可写的文件描述符。
select()函数准备好写的条件:
    1>.套接口有可用于写的空间。
    2>.该连接的写这一半关闭,对这样的套接口进行写操作将产生SIGPIPE信号。
    3>.该套接口使用非阻塞的方式connect建立连接,并且连接已经异步建立,或则connect已经以失败告终。
    4>.其上有一个套接口错误待处理。

exceptfds这个文件集将监视文件集中的任何文件是否发生错误,其实,它可用于其他的用途,例如,监视带外数据OOB,带外数据使用MSG_OOB标志发送到套接字上。当select函数返回的时候,exceptfds将清除其中的其他文件描述符,只留下标记有OOB数据的文件描述符。
timeout本次select()的超时结束时间。usec是微妙的简写,总时间就是妙+微秒。select会修改timeout的值为剩余时间,所以每次调用都必须重	新赋值。
struct timeval {
          time_t       tv_sec;     /* seconds */
          suseconds_t   tv_usec; /* microseconds */
    };
这个参数至关重要,它可以使select处于三种状态:
(1)若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
(2)若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
(3)timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

函数的返回值
正值:表示监视的文件集中有文件描述符符合要求
零值:表示select监视超时
负值:表示发生了错误,错误值由errno指定。

宏操作
FD_ZERO(fd_set *set):          用来清除描述词组set的全部位
FD_SET(int fd,fd_set*set):     用来设置描述词组set中相关fd的位
FD_ISSET(int fd,fd_set *set): 用来测试描述词组set中相关fd 的位是否为真
FD_CLR(inr fd,fd_set* set):    用来清除描述词组set中相关fd 的位


用法

(1)select + accept函数的非阻塞实现
将正在listen的socket设置到readfds中,调用select,如果有客户端connect,select将返回正值,通过宏FD_ISSET可检测到该socket可读,此时再用accept接受新的socket,进行读写.

sockfd = listen_tcp();	//socket()、bind()、listen()
FD_SET(sockfd, rset);
while(1){
	ret = select(sockfd + 1, rset, NULL, NULL, timeout); // 等待某个事件发生:或是新连接、或是数据、或是FIN、或是RST到达
	if(select()返回TIMEOUT){	//select()超时
		printf("日志打印");
		sleep(1);
		continue;
	}
	else if(FD_ISSET(sockfd,&rset)){  	//判断句柄是否可读,返回真代表可读,可读代表有新连接。
		connfd = accept(sockfd, ...);
	}
	else if(select()返回错误){
		return -1;
	}
	pthread_create(thread_recv_data, connfd, ...);	// 创建线程处理新连接.
	close();
}


(2)select + connect函数的非阻塞实现(TCP)

a. 将打开的socket设为非阻塞的。
    b. 发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧在进行还没有完成。
    c. 将打开的socket放进被监视的可写(注意不是可读)文件集合中,用select进行监视,如果可写,查看全局变量errno,如果为零则connect成功。
while(1){
	ret = connect();
	if(errno == EINPROCESS){ //此时TCP的三路握手继续进行
		select(...) //等待某个事件发生:或是新连接、或是超时
		if(FD_ISSET(sockfd,&wset) ){ //判断句柄可写,不能代表建立连接成功。
			getsockopt(...);
			if(errno == 0){  //建立连接成功
			}
		}else if(select()返回TIMEOUT){
			sleep(1);
			continue();
		}
	}
	else if(ret == -1){
		//Connect  failed
	}
	pthread_create(thread_send_log, ...); //建立线程处理新连接
	close();
}

(3)select检测对方Socket连接关闭
    使用select监视是否有数据可读,当监视到可读返回正值后,使用recv函数对套接字进行读操作,recv函数返回正值表示正常的读操作,若返回0或者-1表示对方连接已关闭
int sockfd;
fd_set fdR;
int sock_fd[N];//save client_sockfd, size is N

struct timeval timeout;
 
while(1){ 
    FD_ZERO(&fdR);
    FD_SET(sockfd, &fdR);


    for() FD_SET(sockclient);

    switch (select(sockfd + 1,&fdR, NULL, &timeout)) {
      case -1:error;
      case 0:timeout;
      default:
      if (FD_ISSET(sockclient, &fdR))
	 { 
		ret = recv();
		if(ret <0)
		{
		     "the socket closed"
		} 
      }
     }  
/* if sock_serv is in &fdR, now accept() */
  if( FD_ISSET(sock_serv,&fdR) ){
      sock_fd[..] = now accept();
    }
}


更多:http://blog.163.com/li_xiang1102/blog/static/60714076201110296471767/

   http://blog.csdn.net/hanchaoman/article/details/6184236

   http://www.cnblogs.com/coder2012/archive/2013/06/16/3138530.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值