解析select

来源:互联网 发布:组态王软件介绍 编辑:程序博客网 时间:2024/06/07 06:16


#include<sys/select.h>

#include<sys/time.h>

int select(int maxfdp,fd_set *readset,fd_set *writeset,fd_set *exceptset,struct timeval *timeout);


数据结构:

   fd_set的数据结构,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fe_set的内容,由此来通知执行了select()的进程哪一socket或文件可读。

  过去,一个fd_set通常只能包含<32的fd(文件描述字),因为fd_set其实只用了一个32位矢量来表示fd; 现在,UNIX系统通常会在头文件<sys/select.h>中定义常量FD_SETSIZE,它是数据类型fd_set的描述字数量,其值通常是1024,这样就能表示<1024的fd。

参数:

     nfds: 需要检查的文件描述字个数(即检查到fd_set的第几位),数值应该比三组fd_set中所含的最大fd值更大,一般设为三组fd_set中所含的最大fd值加1(如在readset, writeset, exceptset中所含最大的fd为5,则nfds=6,因为fd是从0开始的 )。设这个值是为了提高效率,使函数不必检查fd_set的所有1024位。

    readset: 用来检查可读性的一组文件描述字。

    writeset: 用来检查可写性的一组文件描述字。

    exceptset: 用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)

    timeout: 有三种可能:

    1.  timeout = NULL (阻塞:直到有一个fd位被置为1函数才返回)

    2.  timeout所指向的结构设为非零时间(等待固定时间:有一个fd位被置为1或者时间耗尽,函数均返回)

    3.  timeout所指向的结构,时间设为0(非阻塞:函数检查完每一个fd后立即返回)

返回值:返回对应位仍然为1的fd的总数。


select调用返回时,除了那些已经就绪的描述符外,select将清除readfds、writefds和exceptfds中的所有没有就绪的描述符。select的返回值有如下情况: 
1.正常情况下返回就绪的文件描述符个数; 
2.经过了timeout时长后仍无设备准备好,返回值为0; 
3.如果select被某个信号中断,它将返回-1并设置errno为EINTR。 
4.如果出错,返回-1并设置相应的errno。

 
系统提供了4个宏对描述符集进行操作: 
#include <sys/select.h> 
#include <sys/time.h> 


 void FD_SET(int fd, fd_set *fdset);  //设置文件描述符集fdset中对应于文件描述符fd的位(设置为1)
void FD_CLR(int fd, fd_set *fdset);   //清除文件描述符集fdset中对应于文件描述符fd的位(设置为0)
void FD_ISSET(int fd, fd_set *fdset);  //检测文件描述符集fdset中对应于文件描述符fd的位是否被设置
void FD_ZERO(fd_set *fdset);    //清除文件描述符集fdset中的所有位(既把所有位都设置为0)

实例:

int socketfd,listenfd,connfd;
int maxfd;
fd_set allset;
struct sockaddr_in servaddr,clientaddr;

listenfd = socket(AF_INET,SOCK_STREAM,0);

bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htol(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);

bind(listenfd,servaddr,sizeof(servaddr));

listen(listenfd,LISTENQ);

maxfd = listenfd;

FD_ZERO(&allset);
FD_SET(listenfd,&allset);

for(;;)
{
 nready = select(maxfd+1,&allset,NULL,NULL,NULL);
 if(FD_ISSET(listenfd,&allset))
 {
  connfd = accept(listenfd,clientaddr,&clilen);
  FD_SET(connfd,&allset);

  }

}
 


我们调用select的时候,将我们关心或感兴趣的描述符,以及等待时长FD_SET告知内核,内核在描述符准备好写,读,异常处理的时候,将会通过selsect函数告知给用户,那些描述符已经准备好,用户在通过FD_ISSET宏来判断哪个描述符是否准备好,并做一些操作。

描述符就绪条件:读条件(1)套接字接收缓冲区中的数据字节数大于等于套接字接收缓冲区低水位标记的当前大小,(2)该链接的读半部关闭(3)该套接字是一个监听套接字且已完成的连接数不为0(4)其上有一个套接字错误待处理。

写条件:(1)套接字接收缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位大小(2)该连接的写半部关闭(3)使用非阻塞式的connect的套接字已建立连接,或者connect以失败告终(4)其上哟一个套接字错误待处理



0 0