Unix网络编程之Select函数

Unix网络编程之Select函数

1. 用处

​ 该函数允许进程知识内核等待多个时间中的任何一个发生,并只在有一个或者多个事件发生或经历一段指定的时间后才唤醒它。

2. 大致原理

​ select需要驱动程序的支持,驱动程序实现fops内的poll函数。select通过每个设备文件对应的poll函数提供的信息判断当前是否有资源可用(如可读或写),如果有的话则返回可用资源的文件描述符个数,没有的话则睡眠,等待有资源变为可用时再被唤醒继续执行。详细的原理请看这里

3. 函数原型

#include <sys/time.h>
#include <unistd.h>
select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);

4. 参数

  • timeout

    它告知内核等待所指定描述符中的任何一个就绪可花多长时间,其具有以下结构

    struct timeval
    {
      time_t tv_sec;     //秒
      time_t tv_usec;    //微秒
    };
    
                            若有就绪描述符则为其数目,若超时则为0,若出错则为-1

    该参数有以下三种可能

    1. 设置为空指针:永远等待下去
    2. 设置具体的值:等待一段固定的时间
    3. 两个参数均为0:根本不等待,既非阻塞
  • fd_set *readfds

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

  • fd_set *writefds , fd_set *errorfds

    用法同上,一个监听是否可以向这些文件中写数据,另一个用来监听文件错误异常。

  • int maxfdp

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

5.fd_set结构体

其实是一个整形数组,其中每个整数中的每一位对应一个描述符,系统提供了下面几个函数对其进行操作。

FD_SET(int fd, fd_set *fdset);       //将fd加入set集合

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

FD_ISSET(int fd, fd_set *fdset);     //检测fd是否在set集合中,不在则返回0

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

6.注意事项

#include    "unp.h"

void
str_cli(FILE *fp, int sockfd)
{
    int         maxfdp1;
    fd_set      rset;
    char        sendline[MAXLINE], recvline[MAXLINE];

    FD_ZERO(&rset);
    for ( ; ; ) {
        FD_SET(fileno(fp), &rset);
        FD_SET(sockfd, &rset);
        maxfdp1 = max(fileno(fp), sockfd) + 1;
        Select(maxfdp1, &rset, NULL, NULL, NULL);

        if (FD_ISSET(sockfd, &rset)) {  /* socket is readable */
            if (Readline(sockfd, recvline, MAXLINE) == 0)
                err_quit("str_cli: server terminated prematurely");
            Fputs(recvline, stdout);
        }

        if (FD_ISSET(fileno(fp), &rset)) {  /* input is readable */
            if (Fgets(sendline, MAXLINE, fp) == NULL)
                return;     /* all done */
            Writen(sockfd, sendline, strlen(sendline));
        }
    }
}

这个程序是unix网络教程书中P133页的程序,刚开始没搞懂它在11行设置了FD_SET(sockfd, &rset),然后又在后面14行检测是否设置了sockfd,为什么要这么做。然后查资料后发现虽然在11行会对sockfd进行设置,如果在设置之后立刻检测,其值也确实为1,但是经过13行Select之后它的值(sockfd的值)会改变,也就是说Select函数会根据实际情况来改变其中描述符的值。下面我举一个例子说明

#include "unp.h"

int mian(int argc, char **argv){
  int sockfd;
  sockfd = Socket(AF_INET, SOCK_STREAM, 0);

  fd_set rset;
  FD_ZERO(&rset);                               //对fd_set进行清零操作
  FD_SET(sockfd, &rset);                        //把sockfd加到rset集合中

  int isset = FD_ISSET(sockfd, &rset);          //加入之后立刻检测
  printf("before select, isset = %d\n", isset); //这里打印的结果为1

  Select(maxfdp1, &rset, NULL, NULL, NULL);     //现在进行select

  isset = FD_ISSET(sockfd, &rset);              //如果sockfd没有准备好的话,那么这里的打印值为0
  printf("after select, isset = %d\n", isset);

  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_24889575/article/details/81385841