select模型的中心思想就是利用select函数,实现对I/O的管理。利用select函数,我们可以判断套接字上是否存在数据,或者能否向一个套接字写入数据。利用这个函数可以防止应用程序在一次I/O的绑定调用中进入锁定状态。
select函数原型:
int select(
__in int nfds, //忽略
__inout fd_set *readfds, //检查可读性
__inout fd_set *writefds, //检查可写性
__inout fd_set *exceptfds, //用于例外数据
__in const struct timeva l *timeout //超时时间,传递NULL会无限期等待下去,0会立刻返回
);
可读性:
1.有数据可以读入。
2.连接已经关闭,重设或者中止。
3.加入调用了listen,而且一个连接正在建立,那么accept函数调用会成功。
关于连接已经关闭,重设或者中止的判断:
当一个套接字在调用了select之后具有可读性,那么这个时候我们可以通过调用recv获得数据。如果真的有数据发送过来,那么这个调用会成功。如果是关闭,重设或者中止,那么recv的调用会失败,这个时候通wsagetlasterror就可以判断连接是否已经中断。
可写性:
1.有数据可以发出。
2.如果已完成了对一个非锁定连接调用处理,连接就会成功。
对于可写性的检查,最好放在需要写数据的时候进行检查。如果和可读性放在同一个地方进行检查,那么select很可能每次都会因为可写性检查成功而返回。
例外数据:
1.加入已完成了对一个非锁定连接调用的处理,连接尝试就会失败。
2.有带外数据可供读写。
fd_set的几个宏:
FD_SETSIZE 定义了fd_set所允许存放套接字的最大个数,默认是64。如果想修改这个默认值,那么在包含winsock2.h之前重新
定义这个宏。最大不要超过1024
FD_CLR(s, *set):从set中删除套接字s。
FD_ISSET(s, *set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。
FD_SET(s, *set):将套接字s加入集合set。
FDZERO(*set):将set初始化成空集合。
select调用流程:
1) 使用FDZERO宏,初始化自己感兴趣的每一个fd_set。
2) 使用FDSET宏,将套接字句柄分配给自己感兴趣的每个fd_set。
3) 调用select函数,然后等待在指定的fd_set集合中,I/O活动设置好一个或多个套接字句柄。select完成后,会返回在所有fd_set集合中设置的套接字句柄总数,它会修改每个fd_set结构,删除那些不存在待决I/O操作的套接字句柄
4) 根据select的返回值,我们的应用程序便可判断出哪些套接字存在着尚未完成(待决)的I/O操作—具体的方法是使用FD_ISSET宏,对每个fd_set集合进行检查。
5) 知道了每个集合中“待决”的I/O操作之后,对I/O进行处理,然后返回步骤1 ),继续进行select处理。
下面是利用之前封装的socket类来实现的一个简单的select服务器,接受用户的连接和消息,在收到消息之后检查可写性,如果可写将收到的消息发还给用户:
Cpp代码
#include
#include