select模型
其使用select函数实现对I/O的管理。select函数可以判断套接字上是否有数据,或者是否能够向套接字上写数据。设计这可函数的目的是,为了防止套接字处于阻塞模式时,I/O调用过程处于阻塞模式;或者当套接字处于非阻塞模式时,产生WSAEWOULDBLOCK错误。如果不满足实现规定的参数条件,那么select函数在进行I/O操作时会阻塞。其定义如下:
- int select(
- int nfds,
- fd_set FAR* readfds,
- fd_set FAR* writefds,
- fd_set FAR* exceptfds,
- const struct timeval FAR* timeout
- );
int select(
int nfds,
fd_set FAR* readfds,
fd_set FAR* writefds,
fd_set FAR* exceptfds,
const struct timeval FAR* timeout
);
- nfds:这个参数会被忽略。
- readfds:其为sd_set类型,其是一系列套接字的集合,用于检查可读性。这个集合要满足下面条件之一:1)有数据可读入 2)连接已经被关闭、重启或终止 3)假如已经调用listen,且有一个连接处于搁置状态,那么accept调用成功。
- wrtiefds:用于检查可写性。其套接字要满足下面条件之一:1)有数据发出 2)如果正在对一个非阻塞连接调用进行处理,则连接就成功了。
- exceptfds:用于带外数据。其套机字要满足下面条件之一:1)加入正在对一个非阻塞连接调用进行处理,连接尝试机会失败。 2)有00B数据可读操作。
- timeout:其一个指向timeval结构体的指针,用于表示select函数在调用返回前的等待时间,如果为空指针({0,0}),表示无限期等待。不为0,表示其中至少一个套接字满足条件。
- 返回值:如果select调用成功,会在fd_set结构中,返回被挂起的I/O操作的所有套接字句柄总量。超时,返回0。失败,返回SOCKET_ERROR。
select返回后,会对每个fd_set结构体进行修改,会将那些不存在被挂起I/O操作的套接字删除。也就是说,我们可以通过FD-ISSET宏来判断等待的套接字是还处于宏中。
timeval结构体定义如下:
- struct timeval
- {
- long tv_sec;
- long tv_usec;
- };
struct timeval
{
long tv_sec;
long tv_usec;
};
- tv_sec:以秒为单位指定等待时间。
- tv_usec:以毫秒为单位指定等待时间。
在用select函数对套接字进行监听前,需要将套接字分配给一个集合。对fd_set集合进行处理与检查的宏:
- FD_ZERO(*,set):将set集合初始化为空。
- FD_CLR(s,* set):从set中删除套接字s.
- FD_ISSET(s,* set):从集合set中检查s是否在其中;是,就返回TRUE。
- FD_SET(s,* set):将套接字s加入集合set中。
- FD_SETSIZE:对fd_set结构中的最多套接字进行设置。因为默认情况下最多能包含64个套接字。
下面是一个框架:
- SOCKET s;
- fd_set fread;
- int ret;
- while(TRUE)
- {
- FD_SERO(&fread);
- FD_SET(s,&fread);
- if((ret=select(0,&fread,NULL,NULL,NULL))==SOCKET_ERROR)
- {
- ....
- }
- if(ret>0)
- {
- if(FD_ISSET(s,&fread))
- {
- }
- }
- }
例如,假定我们想知道是否可从一个套接字中安全地读取数据,同时不会陷于无休止的“锁
定”状态,便可使用F D _ S E T宏,将自己的套接字分配给fd_read集合,再来调用select
。要想检测自己的套接字是否仍属fd_read集合的一部分,可使用FD_ISSET宏。采用下述步骤
,便可完成用select操作一个或多个套接字句柄的全过程:
1) 使用F D _ Z E R O宏,初始化自己感兴趣的每一个 f d _ s e t 。
2) 使用F D _ S E T宏,将套接字句柄分配给自己感兴趣的每个 f d _ s e t 。
3) 调用s e l e c t函数,然后等待在指定的f d _ s e t集合中,I / O活动设置好一个或多个套接字句柄。 s e l e c t完成后,会返回在所有f d _ s e t集合中设置的套接字句柄总数,并对每个集合进行相应的更新。
4) 根据s e l e c t 的返回值,我们的应用程序便可判断出哪些套接字存在着尚未完成(待决) 的I / O操作—具体的方法是使用F D _ I S S E T宏,对每个f d _ s e t集合进行检查。
5) 知道了每个集合中“待决”的I / O操作之后,对I / O进行处理,然后返回步骤 1 )
,继续进 行s e l e c t处理。
s e l e c t返回后,它会修改每个 f d _ s e t结构,删除那些不存在待决 I / O操作的套接字句柄。这正是我们在上述的步骤( 4 ) 中,为何要使用F D _ I S S E T宏来判断一个特定的套接字是否仍在集合中的原因。