理解 select 函数并实现服务器端

时间:2022-03-21 17:57:56

    运用select 函数是最具有代表性的实现复用服务器端方法。Windows平台下也有同名函数提供相同功能,因此具有良好的移植性。

    select函数的功能和调用顺序

    使用select函数时可以将多个文件描述符集中到一起统一监视,项目如下。

    1、是否存在套接字接受数据?

    2、无需阻塞传输数据的套接字有那些?

    3、那些套接字发生了异常?

·   

     select 函数的使用方法与一般函数区别交大,更准确的说,他很难使用,但为了实现I/O复用服务端,我们应该掌握select函数,并运用到套接字编程中。认为“select函数是I/O复用的全部内容”也不为过,select的函数调用方法和顺序如下

                 理解 select 函数并实现服务器端

    设置文件描述符

    利用select 函数可以同时监视多个文件描述符。当然,见识文件描述符可以视为见识套接字。此时首先需要将要监视的文件描述符集中到一起,集中时也要按监视项(接收、传输、异常)区分,即按照上述三种监视项分成3类

    使用fd_set数组变量执行此项操作,该数组是存有0和1的位数组。

   

fd0        fd1      fd2       fd3       fd4

0 1 0 1 0

 

    如果该位为1.则表示该文件描述符是监视对象,如以上fd1 fd3

    FD_ZERO(fd_set *fdset);将fd_set变量的所有位初始化为0

    FD_SET(int fd,fd_set *fdset);在参数fdset直向的变量中注册文件描述符fd的信息。

    FD_CLR(int fd,fd_set *fdset);从参数fdset直向的变量中清清除文件描述符fd的信息

    FD_ISSET(int fd,fd_set *fdset);若参数fdset指向的变量中包含文件描述符fd的信息,则返回真

    FD_ISSET用于验证select函数的调用结果。顺序如下

 

int main()

{

  fd_set set;

  FD_ZERO(&set);     00000......

  FD_SET(1,&set);     01000......

  FD_SET(2,&set);     01100......

  FD_CLR(2,&set);     01000......

}

 

int select(int maxfd,fd_set *readset, fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);

//成功返回大于0的值。失败返回-1

maxfd监视对象描述符数量

readset将所有关注是否存在带读取数据 的文件描述符注册到fd_set上并传递其地址值

writeset将所有关注是否可传输无阻塞数据的文件描述符注册到fd_set上并传递其地址值

exceptset将所有关注是否发生异常的文件描述符注册到fd_set变量上并传递地址

timeout调用select函数后为防止陷入无限阻塞的状态,传递超时信息。

返回值:发生错误时返回-1,超时返回时返回0.因发生关注的时间返回时,返回大于0的值,该值是发生事件的文件描述符。

    如上所述。select函数用来验证3种监视项的变换。根据监视项声明三个fd_set型变量,分别向其注册文件描述信息。并把变量的地址传到上述函数的第二到第四参数。但在此之前(调用select前)需要决定下面2件事。

    第一,文件描述符的监视范围与select函数的第一个参数有关。实际上,select函数要求通过第一个参数传递监视对象文件描述符数量因此,需要得到注册在fd_set变量中的文件描述符数,但每次新建文件描述符时,其值都会增1,故只需将最大的文件描述符值加一在传递到select函数即可。而文件描述符数量起始等于零,所以加一

    第二,select函数的超时时间与最后一个参数有关其定义如下

struct timeval

{

    long tv_sec;   //seconds

    long tv_usec; //mircoseconds

}

    本来select函数只有在监视的文件描述符发生变化时才返回,如果未发生变换,就会进入阻塞状态,指定超时时间是为了防止这种情况发生,通过声明上述结构体变量将秒数填入tv_sec 成员。将毫秒填入tv_usec。然后将结构体的地址传到最后一个参数。此时,即使文件描述符发生变换,只要通过指定时间,也可以从函数返回,不过i这样的情况下,select 函数返回0.因此,可以通过返回值了解返回原因。如果不想设置超时,则传递null参数。

    调用select函数后查看结果

    select返回正整数时,怎么样获知那些文件描述符发生变换?想select函数的第二到第四参数传递fd_set变量中将产生如下变换。

   

fd0       fd1       fd2       fd3       调用select前

0 1 1 1 ..

fd0       fd1       fd2       fd3      (fd1 , fd3 变化时

调用后

0 1 0 1 ...

 

select函数调用完成后,向其传递的fd_set变量中将发生变换,原来为1的所有位均变为0,但发生变换的文件描述符对应位除外。因此,可以认为值仍为1的位置上的描述符发生了变化