select是一种实现IO多路复用的一种方式:一个线程通过记录IO流的状态来同时管理多个IO。
select
函数是 POSIX (可携带操作系统接口) 标准中定义的,用于监视一组文件描述符(file descriptors,简称fd),等待一个或多个fd变为"就绪"状态,即它们上有可读取的数据、可以写入数据,或者出现异常。在网络编程中,select
常被用来实现非阻塞IO和多路复用。
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数说明:
-
nfds
: 是监视的文件描述符集合中最大文件描述符数加1。 -
readfds
: 是待检查其输入是否就绪的文件描述符集合; -
writefds
: 是待检查其输出是否就绪的文件描述符集合; -
exceptfds
: 是待检查是否有异常条件发生的文件描述符集合; -
timeout
: 指定等待就绪文件描述符的时间上限,类型为struct timeval
。如果设为NULL,表示无限期等待;如果设定时间为0秒0微秒,则表示不等待,查询并立即返回。
fd_set
是一个文件描述符集合,由宏来处理:
-
FD_ZERO(fd_set *set)
: 清除fd集合; -
FD_SET(int fd, fd_set *set)
: 将fd加入fd集合; -
FD_CLR(int fd, fd_set *set)
: 将fd从fd集合中清除; -
FD_ISSET(int fd, fd_set *set)
: 检测fd是否在fd集合中。
select
的返回值:
-
如果超时时间内有文件描述符就绪,返回值是就绪文件描述符的总数;
-
如果超时时间到了,还没有文件描述符就绪,返回0;
-
如果出现错误,返回-1,并设置
errno
。
使用select
时需要注意,每次调用后readfds
、writefds
及exceptfds
参数可能会被修改,所以必须在每次调用select
之前重新初始化这些集合。同样,timeout
也可能被修改,因此也需要重新设置。
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
int main() {
fd_set read_fds; //定义文件描述符
struct timeval timeout; //定义超时时间结构体
int ret;
// 初始化fd_set
FD_ZERO(&read_fds);
FD_SET(STDIN_FILENO, &read_fds);
// 设置超时时间
timeout.tv_sec = 10; // 等待10秒
timeout.tv_usec = 0;
while(bRunFlag){ //运行标志
// 等待事件发生或超时
ret = select(STDIN_FILENO + 1, &read_fds, NULL, NULL, &timeout);
if(ret <= 0){ //文件不可读,重新初始化fd_set
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
stKeyTimeout.tv_sec = 10;
continue;
}
if(FD_ISSET(fd, &read_fds)){
ret = read(fd, &, sizeof); //读取
if(ret <= 0){
//读取失败判断
continue;
}
}
return 0;
}