现在的位置: 首页 > 综合 > 正文

关于FD_SET FD_ZERO FD_ZET FD_ISSET Select Socket通信的例子

2014年07月07日 ⁄ 综合 ⁄ 共 2637字 ⁄ 字号 评论关闭

先帖几个链接:

http://www.cppblog.com/CppExplore/archive/2008/03/14/44509.aspx

http://www.cppblog.com/myjfm/archive/2011/10/26/159093.aspx

http://www.cppblog.com/just51living/archive/2011/07/28/151995.html

http://buluzhai.iteye.com/blog/1013451

再次感叹伟大的互联网,下面开始正题!

int select (int maxfdp1,fd_set *readset,fd_set *writeset,
fd_set *exceptset,const struct timeval * timeout);
参数一:为了保持与早期的Berkeley套接字应用程序兼容,一般忽略它,置为0.
参数二:用于检查可读性,
参数三:用于检查可写性,
参数四:用于检查带外数据,
参数五:一个指向timeval结构的指针,用于决定select等待I/o的最长时间。如果为空将一直等待。timeval结构的定义:struct
timeval{
long tv_sec; // seconds
long tv_usec; // microseconds
}
返回值
>0:就绪描述字的正数目
-1:出错
0 :超时

其中 timeout

1.timeout=NULL(阻塞:select将一直被阻,直到某个文件描述符上发生了事件)

       2.timeout所指向的结构设为非零时间(等待固定时间:如果在指定的时间段里有事件发生或者时间耗尽,函数均返回0)



原因:进行Socket通信是如果通过ioctlsocket设置为非阻塞模式时,要进行查询的话,需要while()循环,还有其他方式吗?

Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如
connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等
待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。
可是使用Select就可以完成非阻塞(所谓非阻塞方式non-
block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

解释:

a\ fd_set    fd_set是一组文件描述字(fd)的集合,它用一位来表示个fd

typedef struct fd_set

 {  u_int fd_count;  SOCKET fd_array[FD_SETSIZE];

} fd_set;

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

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

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

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


实例:

socket    s;    
.....    
fd_set    set;    
while(1)    
{        
       FD_ZERO(&set);//将你的套节字集合清空    
       FD_SET(s,    &set);//加入你感兴趣的套节字到集合,这里是一个读数据的套节字s    
       select(0,&set,NULL,NULL,NULL);//检查套节字是否可读,    
                                                         //很多情况下就是是否有数据(注意,只是说很多情况)    
                                                         //这里select是否出错没有写    
       if(FD_ISSET(s,    &set)    //检查s是否在这个集合里面,    
       {                                    //select将更新这个集合,把其中不可读的套节字去掉    
                                           //只保留符合条件的套节字在这个集合里面                           
               recv(s,...);    
       }    
       //do    something    here    
} 


后续

理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
     (1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。
     (2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
     (3)若再加入fd=2,fd=1,则set变为0001,0011
     (4)执行select(6,&set,0,0,0)阻塞等待


 下面还有一个复杂一些的应用:
     //这段代码将指定测试Socket的描述字的可读可写性,因为Socket使用的也是fd
uint32 SocketWait(TSocket *s,bool rd,bool wr,uint32 timems)     
{
      fd_set rfds,wfds;
#ifdef _WIN32
      TIM tv;
#else
      struct tim tv;
#endif     
      FD_ZERO(&rfds);
      FD_ZERO(&wfds); 
      if (rd)      //TRUE
      FD_SET(*s,&rfds);    //添加要测试的描述字 
      if (wr)      //FALSE
        FD_SET(*s,&wfds); 
      tv.tv_sec=timems/1000;      //second
      tv.tv_usec=timems%1000;      //ms 
      for (;;) //如果errno==EINTR,反复测试缓冲区的可读性
           switch(select((*s)+1,&rfds,&wfds,NULL,
               (timems==TIME_INFINITE?NULL:&tv))) //测试在规定的时间内套接口接收缓冲区中是否有数据可读
          {                                               //0--超时,-1--出错
          case 0:     
               return 0; 
          case (-1):    
               if (SocketError()==EINTR)
                    break;               
               return 0; //有错但不是EINTR 
           default:
               if (FD_ISSET(*s,&rfds)) //如果s是fds中的一员返回非0,否则返回0
                    return 1;
               if (FD_ISSET(*s,&wfds))
                    return 2;
               return 0;
          };
}




	

抱歉!评论已关闭.