原创 select函数的作用

2008-8-28 18:17 6617 6 6 分类: 软件与OS

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


有时,select()也被用来当作延时函数使用。sleep()延时会释放cpu,用select的话,可以在占用cpu的情况下,延时


int select(
int nfds,
fd_set* readfds,
fd_set* writefds,
fd_set* exceptfds,
const struct timeval* timeout
);


参数:


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的总数。


四个宏来操作: 完全一点 从accept开始.



    fd_set set;


    FD_ZERO(&set);       /* 将set清零使集合中不含任何fd*/


    FD_SET(fd, &set);    /* 将fd加入set集合 */


    FD_CLR(fd, &set);    /* 将fd从set集合中清除 */


    FD_ISSET(fd, &set); /* 测试fd是否在set集合中*/


     


过去,一个fd_set通常只能包含<32的fd(文件描述 字),因为fd_set其实只用了一个32位矢量来表示fd;现在,UNIX系统通常会在头文件中定义常量 FD_SETSIZE,它是数据类型fd_set的描述字数量,其值通常是1024,这样就能表示<1024的fd。根据fd_set的位矢量实 现,我们可以重新理解操作fd_set的四个宏:



    fd_set set;


FD_ZERO(&set);      /*将set的所有位置0,如set在内存中占8位则将set置为


00000000*/


FD_SET(0, &set);    /* 将set的第0位置1,如set原来是00000000,则现在变为10000000,这样fd==1的文件描述字就被加进set中了 */


FD_CLR(4, &set);    /*将set的第4位置0,如set原来是10001000,则现在变为10000000,这样fd==4的文件描述字就被从set中清除了 */



FD_ISSET(5, &set); /* 测试set的第5位是否为1,如果set原来是10000100,则返回非零,表明fd==5的文件描述字在set中;否则返回0*/


 


首先:
SOCKET sock;
sock= socket(AF_INET,SOCK_STREAM,0);


struct sockaddr_in addr;      //告诉sock 应该再什么地方licence
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(11111);   //端口啦
addr.sin_addr.s_addr=htonl(INADDR_ANY);            //在本机的所有ip上开始监听


bind (sock,(sockaddr *)&addr,sizeof(addr));//bind....


listen(sock,5);                               ;//最大5个队列


SOCKET socka;                         //这个用来接受一个连接
fd_set rfd;                                   // 描述符集 这个将用来测试有没有一个可用的连接
struct timeval timeout;


FD_ZERO(&rfd);                     //总是这样先清空一个描述符集


timeout.tv_sec=60;                //等下select用到这个
timeout.tv_usec=0;


u_long ul="1";


ioctlsocket(sock,FIONBIO,&ul);    //用非阻塞的连接


//现在开始用select
FD_SET(sock,&rfd);    //把sock放入要测试的描述符集 就是说把sock放入了rfd里面 这样下一步调用select对rfd进行测试的时候就会测试sock了(因为我们将sock放入的rdf) 一个描述符集可以包含多个被测试的描述符,
if(select(sock+1,&rfd,0,0, &timeout)==0)   // select的第一个参数是可以忽略的(这样写是为了保持和linux下一致) 第二个参数放入需要测试的读描述符集(也就是说如果这里面有一个描述符可以读取了,select就返回) 第三个放入需要测试的写描述符集,第四个放入"可执行描述符集"(??我也不知道) 第五个参数是超时时间(如果过了这个超时时间依然没有描述符准备好,select也返回.(如果为NULL,那就一直等到一个描述符集变成准备好的状态)
{ //这个大括号接上面的,返回0那么就超过了timeout预定的时间


//处理....


}


if(FD_ISSET(sock,&rfd))
{      //有一个描述符准备好了


socka=accept(sock,0,0);     //好了 接受它吧


//你还要判断一下socka是不是有效的socket才行....


-------------------------------------------------------------------------------------------------------------------------------


一般的情况下


假设你要判断两个socket 是否可读可写 那就这样:


假设 socka 和sockb 是两个socket 他们已经被连接上,并且能够收发数据


fd_set rfd,wfd;//一个用来测试读 一个用来测试写


FD_ZERO(&rfd);


FD_ZERO(&wfd);


FD_SET(socka,&rfd);//把socka放入读描述符集


FD_SET(sockb,&rfd);//把sockb放入读描述符集


FD_SET(socka,&wfd);把socka放入写描述符集


FD_SET(sockb,&wfd);把sockb放入写描述符集


if(SOCKET_ERROR!=select(0,&rfd,&wfd,0,0))      //测试这两个描述符集,永不超时 其中rfd只用来测试读 wfd只用来测试写


{      //没有错误


if(FD_ISSET(socka,&rfd))    //socka可读


{...}


if(FD_ISSET(sockb,&rfd)   //sockb可读


{...}


if(FD_ISSET(socka,&wfd) //socka 可写


{...}


if(FD_ISSET(sockb,&wfd) //sockb可写


{...}


}



.............................................................................................



下面是linux环境下select的一个简单用法


#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude


int main ()
{
int keyboard;
int ret,i;
char c;
fd_set readfd;
struct timeval timeout;
keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
assert(keyboard>0);
while(1)
    {
timeout.tv_sec=1;
timeout.tv_usec=0;
FD_ZERO(&readfd);
FD_SET(keyboard,&readfd);
ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);
if(FD_ISSET(keyboard,&readfd))
    {
      i="read"(keyboard,&c,1);
          if(''\n''==c)
          continue;
      printf("hehethe input is %c\n",c);
    
       if (''q''==c)
      break;
      }
}
}
用来循环读取键盘输入

文章评论0条评论)

登录后参与讨论
我要评论
0
6
关闭 站长推荐上一条 /2 下一条