运用select 函数是最具有代表性的实现复用服务器端方法。Windows平台下也有同名函数提供相同功能,因此具有良好的移植性。
select函数的功能和调用顺序
使用select函数时可以将多个文件描述符集中到一起统一监视,项目如下。
1、是否存在套接字接受数据?
2、无需阻塞传输数据的套接字有那些?
3、那些套接字发生了异常?
·
select 函数的使用方法与一般函数区别交大,更准确的说,他很难使用,但为了实现I/O复用服务端,我们应该掌握select函数,并运用到套接字编程中。认为“select函数是I/O复用的全部内容”也不为过,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的位置上的描述符发生了变化