函数原型:
nt select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
经典的Unix select系统调用:
for( ;; )
{
tv.tv_sec = 30;
tv.tv_usec = 0;
FD_ZERO( &rfds );
FD_SET( sockfd, &rfds );
switch( ret = select( sockfd + 1, &rfds, NULL, NULL, &tv ) )
{
case 0:
return TIMEOUT;
case -1:
if ( (errno == EINTR) )
continue;
return BROKEN;
default:
if ( FD_ISSET( sockfd, &rfds ) )
{
...
}
}
}
阻塞与非阻塞的理解:
所谓阻塞方式(block),顾名思义,就是进程或是线程执行到这些函数时,必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回。
所谓非阻塞方式 (non-block),就是进程或线程执行此函数时,不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况。
Tip:
若事件发生,则与阻塞方式相同;若事件没有发生,则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高
代码示例:
main()
{
int sock;
FILE *fp;
struct fd_set fds;
struct timeval timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0
char buffer[256]={0}; //256字节的接收缓冲区
while(1)
{
FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化
FD_SET(sock,&fds); //添加描述符
FD_SET(fp,&fds); //同上
maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1
switch(select(maxfdp,&fds,&fds,NULL,&timeout)) //select使用
{
case -1:
exit(-1);
break; //select错误,退出程序
case 0:
break; //再次轮询
default:
if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据
{
recvfrom(sock,buffer,256,.....);//接受网络数据
if(FD_ISSET(fp,&fds)) //测试文件是否可写
fwrite(fp,buffer...);//写入文件
//buffer清空;
}// end if break;
}// end switch
}//end while
}//end main