- 前面介绍的函数如recv、send、read和write等函数都是阻塞性函数,若资源没有准备好,则调用该函数的进程进入阻塞状态,下面是两种I/O多路复用的解决方案。
- fcntl函数实现(将套字设置为非阻塞方式)
- select函数实现
- poll函数实现
1、 fcntl函数实现(非阻塞方式)
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
功能:对打开的文件描述符fd执行描述的操作,操作由cmd决定。
2、I/O多路转换select函数
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout,const sigset_t *sigmask);
1)、参数
maxfdp1
:最大fd加1(max fd plus 1),在三个描述符集中找出最高描述符编号值,然后加1,这就是第第一参数值。(即要监听的所有套接子中,最大的+1)readfds、writefds和exceptfds
:是指向描述符集的指针。这三个描述符集说明了我们关心的可读,可写或处于异常条件的各个描述符。每个描述符集存放在一个fd_set数据类型中。
2)、select函数根据希望进行的文件操作对文件描述符进行分类处理,这里,对文件描述符的处理主要 设计4个宏函数
FD_ZERO(fd_set *set)
清除一个文件描述符集;FD_SET(int fd, fd_set *set)
将一个文件描述符加入文件描述符集中FD_CLR(int fd, fd_set *set)
将一个文件描述符从文件描述符中清除FD_ISSET(int fd,fd_set *set)
测试该集中的一个给定位是否有变化(返回0表示,该fd没有准备好,返回1表示该fd准备好了)
3)、在使用select函数之前,首先使用FD_ZERO和FD_SET来初始化文件描述符集,并使用select函数时,可循环使用FD_ISSET测试描述符集,在执行完成对应的文件描述符后,使用FD_CLR来清除描述符集。
4)、总体描述
①select IO多路复用,可以同时监听多个文件描述符,若至少一个文件描述符状态变为“准备”状态,则select被唤醒,
②select函数中,若timeval时间到,则被唤醒,timeval若为NULL,则没有时间,永久阻塞,等待套接字准备状态。
③其它情况下,select函数,保持阻塞,等待被唤醒
④select函数,在监听套按字的时候,根据传参的fd_set判断,需要监听哪些套接字,比如第n位为1,则n会被监听,若m位为0,则m不监听
⑤被唤醒后,根据返回值可以判断是否套接字“准备”状态唤醒,确定是套接字唤醒后,如何判断是哪个套接字,多少个套接字造成的唤醒?
- 返回值大于0,返回结果为多少个套接字造成的唤醒
- 哪个造成的唤醒,使用FD_ISSET来判断。
3、poll函数
poll的实现和select非常相似,只是描述fd集合的方式不同。
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /*文件描述符*/
short events; /*监控的事件*/
short revents; /*返回的事件*/
};
参数:
- fds:struct pollfd类型的数组, 存储了待检测的文件描述符,struct pollfd有三个成员:
- fd:委托内核检测的文件描述符 events:委托内核检测的fd事件(输入、输出、错误),每一个事件有多个取值
- revents:这是一个传出参数,数据由内核写入,存储内核检测之后的结果 nfds:描述的是数组 fds 的大小 timeout:
- 指定poll函数的阻塞时长
- -1:一直阻塞,直到检测的集合中有就绪的IO事件,然后解除阻塞函数返回
- 0:不阻塞,不管检测集合中有没有已就绪的IO事件,函数马上返回
- 大于0:表示 poll 调用方等待指定的毫秒数后返回