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

Linux中select函数的使用 select() Linux linux函数 select

2013年09月14日 ⁄ 综合 ⁄ 共 6424字 ⁄ 字号 评论关闭

阻塞式I/O编程有两个特点:

一、如果一个发现I\O有输入,读取的过程中,另外一个也有了输入,这时候不会产生任何反应.这就需要你的程序语句去用到select函数的时候才知道有数据输入。

二、程序去select的时候,如果没有数据输入,程序会一直等待,直到有数据位置,也就是程序中无需循环和sleep。

 

Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序

(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。

可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

下面man下select函数的:

/* According to POSIX.1-2001 */

      #include <sys/select.h>

      /* According to earlier standards */

      #include <sys/time.h>

      #include <sys/types.h>

      #include <unistd.h>

      int select(int nfds, fd_set *readfds, fd_set *writefds,

                  fd_set *exceptfds, structtimeval *timeout);

第一,   struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Linux下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。

fd_set集合可以通过一些宏由人为来操作,

比如

清空集合FD_ZERO(fd_set*);

将一个给定的文件描述符加入集合之中FD_SET(int,fd_set *);

将一个给定的文件描述符从集合中删除FD_CLR(int,fd_set*);

检查集合中指定的文件描述符是否可以读写FD_ISSET(int,fd_set* )。
第二,structtimeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。

具体解释select的参数:

int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!

 
fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。

fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。

struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,

第一,   若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;

第二,   第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;

第三,   timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,文件无变化返回0,有变化返回一个正值;

返回值:

负值:select错误

正值:某些文件可读写或出错

 0:等待超时,没有可读写或错误的文件

举个简单的例子:

#include <sys/time.h>

#include <sys/types.h>

#include <unistd.h>

#include <sys/select.h>

#include<stdio.h>

#include<stdlib.h>

int main()

{

         fd_setreadfds;

         FD_ZERO(&readfds);

         FD_SET(STDIN_FILENO,&readfds);

         intret;

         charbuf[256]={0};

         structtimeval  tv={5,1000};

         //       tv.tv_sec=5;

         //       tv.tv_usec=1000;

 

         ret=select(STDIN_FILENO+1,&readfds,NULL,NULL,&tv);

         //ret=select(STDIN_FILENO+1,&readfds,NULL,NULL,NULL);

         //ret=select(STDIN_FILENO+1,&readfds,NULL,NULL,0);

         printf("ret=%d\n",ret);

         if(ret==-1)

         {

                   perror("selsecterror ");

                   exit(EXIT_FAILURE);

         }

         elseif(ret)

         {

                   if(FD_ISSET(STDIN_FILENO,&readfds))

                            read(STDIN_FILENO,buf,256);

                   printf("readfrom stdin msg : %s\n",buf);

         }

         else

                   printf("timeout\n");

 

         return0;

 

}

运行结果:3中状态:

1.    错误。

2.    超时,在指定的时间内没有检测到用户的输入。Selsect()返回0如图:

                             

3.用户输入了,即检测到了标准输入已经准备好了

 

下面我们看一下socket编程中select()函数的使用。

在此之贴出select函数部分。

客户端:

while(1)
    {
         FD_ZERO(&readfds);
        FD_SET(sockfd,&readfds);
        FD_SET(STDIN_FILENO,&readfds);
        maxfd=sockfd;
        printf("before select %d\n",readfds);
      if((ret=select(maxfd+1,&readfds,NULL,NULL,NULL))==-1)
      { 
          perror("select:");
          exit(EXIT_FAILURE);
      }
      //哪个文件描述符准备好了就将那个所对应的位设置为1,其他的设置为0
      //eg:socket=3准备好了则readfds为00000100(8)
      //select 函数的返回值为:准备好的文件描述符的个数。
         DEBUG("ret=%d\n",ret);
        printf("after select %d\n",readfds);
  
     if(FD_ISSET(sockfd,&readfds))
        { printf("sockfd select %d\n",readfds);
           if(read(sockfd,read_buf,sizeof(read_buf))<=0)
           {
             perror("read <=0 ");
             break;
           }
          printf("%s\n",read_buf);
          memset(read_buf,0,sizeof(read_buf));
        }    
        if(FD_ISSET(STDIN_FILENO,&readfds)) //有标准输入描述符准备好了
        {  printf("STDIN_FILENO select %d\n",readfds);
         
           read(STDIN_FILENO,buf,sizeof(buf));   
          //或 scanf("%s",buf);//从标准输入设备的缓冲区中取出内容放到buf中
          write(sockfd, buf, strlen(buf)+1);
        }
         
    }

服务端:

void server_write_read(int sockfd)
{

      fd_set  rfds;
    FD_ZERO(&rfds); 
    FD_SET(sockfd,&rfds);
    int i, maxfd = sockfd,ret=-1;
    int newfd;
    struct sockaddr_in client;
   socklen_t len = sizeof(client);
   char buf[MAXBUF] = {'\0'};
    while(1)
    {
             FD_ZERO(&rfds);  //clear set
             for(i = 0; i < MAXCLIENT; i++)
           {
              // add fd to set
              if(connfd[i].fd != 0)
              {
                FD_SET(connfd[i].fd ,&rfds);
              }
              //get tje max fd
              if(connfd[i].fd != 0  &&connfd[i].fd> maxfd)
              {
                maxfd=connfd[i].fd;
               
              }
            }
         FD_SET(sockfd,&rfds);
         printf("server_write_read  before select %d\n",rfds);
         if((ret=select(maxfd+1,&rfds,NULL,NULL,NULL))==-1)
       { 
           perror("select:");
           continue;
       }
       printf("server_write_read  after select %d\n",rfds);
       if(FD_ISSET(sockfd, &rfds))
       {       printf("server_write_read  sockfd %d\n",rfds);
           newfd = accept(sockfd, (struct sockaddr *)&client, &len);
           if(-1 == newfd)
            system_error("accept");
            else
             printf("welcome  %s was connected.\n", inet_ntoa(client.sin_addr));
          DEBUG("newfd=%d\n",newfd);
           //save socket description
           for(i = 0; i < MAXCLIENT; i++)
           {
              if(connfd[i].fd == 0)
              {
                 connfd[i].fd = newfd;
                 break;
              }
            }
        
     }
  
   for(i = 0; i < MAXCLIENT; i++)
   { 
      if(FD_ISSET(connfd[i].fd, &rfds))
      {
            if(connfd[i].fd == 0)continue;
         if ((read(connfd[i].fd, buf, MAXBUF)) <= 0)
           {DEBUG("server read <=0\n");
             for(i = 0; i < MAXCLIENT; i++)
             {
               if(connfd[i].fd==newfd)
                {
                  connfd[i].fd=0;
                  break;
                }
             }
             close(newfd);
             FD_CLR(newfd, &rfds);
           }
           else
           {
               int j=0;
               printf("////////// buf=%s\n",buf);
              for(; chat_func[j].func; j++)
              {
                if (buf[0] == chat_func[j].protocol)
                {
                 chat_func[j].func(buf, connfd[i].fd);
                 break;
                }
             }
             memset(buf,0,MAXBUF); 
           } 
      }
    
    }
       
   }
  
}

 

 

抱歉!评论已关闭.