select,多路同步I/O模型

来源:互联网 发布:qq群优化排名手机软件 编辑:程序博客网 时间:2024/05/21 19:37

 1)原理介绍。
select(选择)模型是socket中最常见的I/O模型。之所以称其为“select模型”,是由于它的“中心思想”是利用select函数实现对I/O的管理!
select模型使那些想避免在套接字调用过程中被无辜“锁定”的应用程序,采取一种有序的方式,同时进行对多个套接字的管理。设计这个函数,唯一的目的便是防止应用程序在套接字处于锁定模式中时,在一次I/O绑定调用(如send或recv)过程中,被迫进入“锁定”状态;同时防止在套接字处于非锁定模式中时,产生WSAEWOULDBLOCK错误。
select()让你可以同时监视多个套接字。如果你想知道的话,那么它就会告诉你哪个套接字准备读,哪个又准备写,哪个套接字又发生了例外(exception)。它监视一系列文件描述符,甚至标准输入输出或者其他的端口,特别是readfds、writefds和exceptfds。

2)相关函数。
该函数的原型是:
int select(int numfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
在三个参数中(readfds、writefds和exceptfds),任何两个都可以是空值(NULL);但是,至少有一个不能为空值!在任何不为空的集合中,必须包含至少一个套接字句柄;否则,select函数便没有任何东西可以等待。
select()函数的副作用是:select返回后,它会修改每个fd_set结构,删除那些不存在待决I/O操作的套接字句柄。
对fd_set进行处理与检查的宏:
■FD_CLR(s,*set):从set中删除套接字s。
■FD_ISSET(s,*set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。
■FD_SET(s,*set):将套接字s加入集合set。
■FD_ZERO(*set):将set初始化成空集合。

3)处理范围。
readfds集合包括符合下述任何一个条件的套接字:
■有数据可以读入。
■连接已经关闭、重设或中止。
■假如已调用了listen,而且一个连接正在建立,那么accept函数调用会成功。
writefds集合包括符合下述任何一个条件的套接字:
■有数据可以发出。
■如果已完成了对一个非锁定连接调用的处理,连接就会成功。
exceptfds集合包括符合下述任何一个条件的套接字:
■假如已完成了对一个非锁定连接调用的处理,连接尝试就会失败。
■有带外(Out-of-band,OOB)数据可供读取。

4)使用流程和步骤。
标准流程是:
假定我们想测试一个套接字是否“可读”:首先将自己的套接字增添到readfds集合;然后等待select函数完成;select完成之后,必须判断自己的套接字是否仍为readfds集合的一部分;最后采取特定的处理措施。
采用下述步骤,便可完成用select操作一个或多个套接字句柄的全过程:
(1)使用FD_ZERO宏,初始化自己感兴趣的每一个fd_set。
(2)使用FD_SET宏,将套接字句柄分配给自己感兴趣的每个fd_set。
(3)调用select函数,然后等待在指定的fd_set集合中,I/O活动设置好一个或多个套接字句柄。select完成后,会返回在所有fd_set集合中设置的套接字句柄总数,并对每个集合进行相应的更新。
(4)根据select的返回值,我们的应用程序便可判断出哪些套接字存在着尚未完成(待决)的I/O操作—具体的方法是使用FD_ISSET宏,对每个fd_set集合进行检查。
(5)知道了每个集合中“待决”的I/O操作之后,对I/O进行处理,然后返回步骤1),继续进行select处理。

5)扩展话题。
若想在这个应用程序中添加更多的套接字(比如监听多个client的socket),只需为额外的套接字维护它们的一个列表,或维护它们的一个数组即可。在主线程中进行accept的等待,然后为每个会话而不是socket来建立线程或者复用线程池,在线程中处理send、recv等数据相关的操作。
最后一件关于select()的事情:如果你有一个正在侦听(listen())的套接字,你可以通过将该套接字的文件描述符加入到readfds集合中来看是否有新的连接。

原创粉丝点击