选择(select)模型是Winsock中最常见的 I/O模型。核心便是利用 select 函数,实现对 I/O的管理!利用 select 函数来判断某Socket上是否有数据可读,或者能否
向一个套接字写入数据,防止程序在Socket处于阻塞模式中时,在一次 I/O 调用(如send或recv、accept等)过程中,被迫进入“锁定”状态;同时防止在套接字处于非
阻塞模式中时,产生WSAEWOULDBLOCK错误。
select 的函数原型如下:
int select(
__in int nfds, //会被忽略,只是为了保持与Berkeley套接字兼容
__in_out fd_set* readfds, //检查可读性
__in_out fd_set* writefds, //检查可写性
__in_out fd_set* exceptfds, //例外数据
__in const struct timeval* timeout //超时时间
);
其中,fd_set的结构定义为:
typedef struct fd_set {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE]; //#define FD_SETSIZE 64,故最多可以监视64个套接字
} fd_set;
下面,我们来看看那三个输入输出参数各表示什么意思
readfds:readfds 集合包括符合下述任何一个条件的套接字
1、有数据可以读入。
2、连接已经关闭、重设或中止。
3、假如已调用了listen,而且一个连接正在建立,那么accept函数调用会成功。
writefds:writefds 集合包括符合下述任何一个条件的套接字
1、有数据可以发出。
2、 如果已完成了对一个非锁定连接调用的处理,连接就会成功。
exceptfds:exceptfds 集合包括符合下述任何一个条件的套接字
1、假如已完成了对一个非锁定连接调用的处理,连接尝试就会失败。
2、有带外(Out-of-band,OOB)数据可供读取。
在这三个参数中(readfds、writefds 和 exceptfds),任何两个都可以是空值( NULL);但是,至少有一个不能为空值!在任何不为空的集合中,必须包含至少一
个套接字句柄;否则, select 函数便没有任何东西可以等待。最后一个参数 timeout 对应的是一个指针,它指向一个timeval 结构,用于决定select 最多等待 I/O操作
完成多久的时间。如 timeout 是一个空指针,那么 select 调用会无限期地“锁定”或停顿下去,直到至少有一个描述符符合指定的条件后结束。
timeval 结构的定义如下:
tv_sec 字段以秒为单位指定等待时间;
tv_usec 字段则以毫秒为单位指定等待时间。
1秒 = 1000毫秒
select 成功完成后,会在 fdset 结构中,返回刚好有未完成的 I/O操作的所有套接字句柄的总量。若超过 timeval 设定的时间,便会返回0。若 select 调用失败,都
会返回 SOCKET_ERROR,应该调用 WSAGetLastError 获取错误码!
用 select 对套接字进行监视之前,必须将套接字句柄分配给一个fdset的结构集合,之后再来调用 select,便可知道一个套接字上是否正在发生上述的 I/O 活动。
Winsock 提供了下列宏操作,可用来针对 I/O活动,对 fdset 进行处理与检查:
FD_CLR(s, *set):从set中删除套接字s。
FD_ISSET(s, *set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。
FD_SET(s, *set):将套接字s加入集合set。
FD_ZERO( * set):将set初始化成空集合。
select 函数返回后,会修改 fdset 结构,删除那些不存在待决 I/O 操作的套接字句柄。
select函数具体流程步骤如下:
1、使用FDZERO宏,初始化一个fdset对象。
2、使用FDSET宏,将套接字句柄加入到fdset集合中。
3、调用 select 函数,等待其返回……select 完成后,会返回在所有 fdset 集合中设置的套接字句柄总数,并对每个集合进行相应的更新。
4、根据 select的返回值和 FDISSET宏,对 fdset 集合进行检查。
5、知道了每个集合中“待决”的 I/O操作之后,对 I/O进行处理。
6、然后返回步骤1,继续进行 select 处理。
在代码的实现中,我封装了一个SelectSocket函数用于对select函数的操作,该函数定义如下:
BOOL SelectSocket(SOCKET s, int iTimeOut, BOOL bRead) { ASSERT(s != INVALID_SOCKET); fd_set fd; FD_ZERO(&fd); FD_SET(s, &fd); timeval time; time.tv_sec = iTimeOut / 1000; time.tv_usec = iTimeOut % 1000; int iFlg; if (bRead) { iFlg = select(0, &fd, NULL, NULL, &time); } else { iFlg = select(0, NULL, &fd, NULL, &time); } if (iFlg <= 0) { return FALSE; } else if (FD_ISSET(s, &fd)) { return TRUE; } return FALSE; }
这里我就不演示界面交互了,在我的另一篇随笔,<<异步选择模型>>中,我会用select函数实现的Client与用WSAAsyncSelect函数实现的Server进行交互,那时我
们再看看这种模型的具体实现过程。
http://www.cnblogs.com/venow/archive/2012/06/09/2543053.html