C++中使用select实现IO多路复用,通过调用select()函数监控多个文件描述符的读写状态,结合fd_set宏操作管理集合,示例程序监听socket和标准输入,每次循环重置集合并调用select等待事件,支持超时机制,但存在性能瓶颈和fd数量限制,适用于小型或跨平台项目。

在C++中使用IO多路复用的 select 方法,主要是通过调用操作系统提供的 select() 系统函数来实现。它允许程序同时监控多个文件描述符(如socket、标准输入等),判断哪些已经准备好进行读、写或出现异常事件。这对于编写高性能网络服务器或需要处理多个并发连接的程序非常有用。
select 函数基本用法
select() 的函数原型定义在 <sys/select.h> 头文件中:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数说明:
-
nfds:所有被监控的文件描述符中最大值加1(即从0到nfds-1)
-
readfds:监听可读事件的文件描述符集合
-
writefds:监听可写事件的文件描述符集合
-
exceptfds:监听异常事件的文件描述符集合
-
timeout:等待超时时间,可以设为阻塞(NULL)、非阻塞(tv_sec=0, tv_usec=0)或指定超时
fd_set 集合操作宏
select 使用 fd_set 类型来管理文件描述符集合,配合以下宏操作:
立即学习“C++免费学习笔记(深入)”;
-
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):检查文件描述符是否在集合中(select 返回后使用)
C++ 示例:监听标准输入和 socket
下面是一个简单的 C++ 示例,演示如何使用 select 监听标准输入和一个 socket 连接:
#include <iostream>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
// 创建 socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);
bind(server_fd, (struct sockaddr *)&address, sizeof(address));
listen(server_fd, 3);
std::cout << "等待连接...\n";
new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
fd_set readfds;
struct timeval timeout;
while (true) {
// 每次循环都要重新设置 fd_set
FD_ZERO(&readfds);
FD_SET(new_socket, &readfds);
FD_SET(STDIN_FILENO, &readfds); // 监听标准输入
int max_fd = (new_socket > STDIN_FILENO ? new_socket : STDIN_FILENO) + 1;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
int activity = select(max_fd, &readfds, nullptr, nullptr, &timeout);
if (activity < 0) {
std::cerr << "select 错误\n";
break;
} else if (activity == 0) {
std::cout << "select 超时\n";
continue;
}
// 检查 socket 是否可读
if (FD_ISSET(new_socket, &readfds)) {
int valread = read(new_socket, buffer, 1024);
if (valread <= 0) {
std::cout << "客户端断开\n";
break;
}
std::cout << "收到数据: " << buffer << "\n";
memset(buffer, 0, 1024);
}
// 检查标准输入是否可读
if (FD_ISSET(STDIN_FILENO, &readfds)) {
std::string input;
std::getline(std::cin, input);
const char* msg = input.c_str();
send(new_socket, msg, strlen(msg), 0);
}
}
close(new_socket);
close(server_fd);
return 0;
}
注意事项与局限性
尽管 select 是跨平台兼容性较好的 IO 多路复用方式,但也有明显缺点:
- 每次调用 select 都需要重新设置 fd_set 集合
- 文件描述符数量受限(通常最多 1024)
- 需要遍历所有监听的 fd 来检查状态变化,效率随 fd 数量增加而下降
- 每次都要传递最大 fd + 1,开销较大
在 Linux 下,更推荐使用 poll 或 epoll 实现更高性能的多路复用。
基本上就这些。select 虽然古老,但在小型项目或跨平台场景下仍很实用。
以上就是c++++怎么操作IO多路复用select_c++ IO多路复用select方法的详细内容,更多请关注php中文网其它相关文章!