Select在Socket编程中还是比较重要的,它能够监视我们需要监视的文件描述符的变化情况——读、写或是异常。
Select的函数格式(Unix系统下的伯克利socket编程,和windows下的略有区别,体现两个方面:一是select函数的第一个参数,在windows下可以忽略,但在linux下必须设为最大文件描述符加1;二是结构fd_set在两个系统里定义不一样)。
int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
Windows下使用心得:
int maxfdp 是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1。在Windows中这个参数的值无所谓,可以设置任意值,如0。
fd_set *readfds 是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
fd_set *writefds同上面一个参数的意图,是要监视这些文件描述符的写变化的。
fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。
struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态:
第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
以监听读变化的简单案例为例:
fd_set fdread; //定义read集合
FD_ZERO(&fdread); //清空集合
FE_SET(g_Socket[i], &fdread); //将所有已连接的套接字加入集合
// 仅关注read变化,监听fdread集合中的所有套接字的read变化
select_ret = select(0, &fdread, NULL, NULL, &tv);
if(select_ret == 0)
{
//超时;
continue;
}
if(select_ret < 0)
{
//监听出错,比如fdread集合为空
continue;
}
if(select_ret > 0)
{
//监听到有read变化
//fdread集合仅将有read变化的套接字保留,其余删除
}
//遍历数组中的所有套接字
for (i = 0; i < g_Total; i++)
{
//确认套接字是否在fdread集合中,是的话则开始read操作。
if (FD_ISSET(g_Socket[i], &fdread))
{
//读操作
}//if
}//for
//继续监听read变化