news 2026/1/27 4:58:32

C++:实现多路复用select模型实例(附带源码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++:实现多路复用select模型实例(附带源码)

一、项目背景详细介绍

在网络服务器开发中,如何同时处理多个客户端连接是一个绕不开的核心问题。

在最初级的网络程序中,服务器通常采用:

  • 阻塞式单客户端模型

  • 一个客户端一个进程 / 线程

这种模型在客户端数量很少时可以工作,但一旦并发连接数增多,就会暴露出明显缺陷:

  • 线程 / 进程数量快速增长

  • 上下文切换成本高

  • 系统资源消耗巨大

  • 程序可扩展性差

为了解决单线程同时监听多个 I/O 事件的问题,操作系统引入了
I/O 多路复用(I/O Multiplexing)机制

在 Linux/Unix 系统中,I/O 多路复用主要有三种实现:

  1. select(最早、最经典)

  2. poll

  3. epoll(高性能版本)

虽然select 在性能和规模上不如 epoll
但它具有以下重要价值:

  • 接口简单,容易理解

  • 跨平台性极好(Linux / Unix / Windows)

  • 是理解 poll / epoll 的理论基础

因此,select 模型是学习高性能网络编程的第一块基石

本项目目标是:

使用 C++ 实现一个基于 select 的多路复用服务器示例,完整展示其工作原理


二、项目需求详细介绍

2.1 功能需求

  1. 基于TCP 协议

  2. 使用select 模型实现 I/O 多路复用

  3. 支持多个客户端同时连接

  4. 服务端能够:

    • 接收客户端数据

    • 原样回显(Echo)

  5. 客户端断开时正确处理并释放资源


2.2 技术要求

  • 平台:Linux / Unix

  • 使用系统原生:

    • select

    • fd_set

  • 阻塞式 select(教学清晰)

  • 单线程事件循环

  • 代码结构清晰、注释详尽


2.3 设计要求

  • 使用 C++ 编写

  • 所有代码集中在一个代码块

  • 使用注释模拟“多文件”结构

  • 关键逻辑必须有中文解释

  • 适合课堂逐行讲解


三、相关技术详细介绍

3.1 什么是 I/O 多路复用

I/O 多路复用的本质是:

一个线程,通过系统调用,同时监听多个文件描述符的 I/O 状态

当任意一个文件描述符就绪时,系统调用返回,程序再逐个处理就绪的描述符。


3.2 select 模型的基本思想

select 模型的核心思想可以总结为一句话:

“我把所有关心的 fd 告诉内核,你帮我看着,有动静再叫我。”

其核心流程如下:

  1. 将所有需要监听的 fd 放入集合

  2. 调用select阻塞等待

  3. select返回后,遍历 fd 集合

  4. 处理就绪的 fd


3.3 select 的核心数据结构:fd_set

fd_set本质是一个位图结构,用于表示哪些 fd 需要被监听。

常用宏:

FD_ZERO(&set); // 清空集合 FD_SET(fd, &set); // 添加 fd FD_CLR(fd, &set); // 移除 fd FD_ISSET(fd, &set);// 判断 fd 是否就绪


3.4 select 的局限性

虽然 select 易学,但存在明显缺点:

  • 最大 fd 数量受限(通常 1024)

  • 每次调用都要全量遍历

  • 用户态和内核态频繁拷贝 fd_set

这也是后续poll / epoll 出现的根本原因


四、实现思路详细介绍

4.1 整体架构思路

  1. 创建监听 socket

  2. 初始化 fd_set

  3. 将监听 socket 加入 fd_set

  4. 循环调用 select:

    • 判断监听 socket 是否就绪(新连接)

    • 判断客户端 socket 是否就绪(收发数据)

  5. 客户端断开时从 fd_set 中移除


4.2 核心事件分类

在 select 服务器中,主要处理两类事件:

1️⃣ 监听 socket 可读

  • 表示有新的客户端连接

  • 调用accept接收连接

  • 将新的客户端 fd 加入 fd_set


2️⃣ 客户端 socket 可读

  • 表示客户端发送了数据

  • 调用recv接收数据

  • 原样回显(Echo)

  • 客户端关闭时清理资源


4.3 fd 管理策略

  • 使用一个数组保存所有客户端 fd

  • 维护当前最大 fd 值(select 需要)

  • 断开连接时及时移除 fd


五、完整实现代码

/**************************************************** * 文件名:SelectServer.cpp * 描述:C++ 基于 select 的多路复用模型示例 ****************************************************/ #include <iostream> #include <cstring> #include <unistd.h> #include <arpa/inet.h> #include <sys/select.h> using namespace std; const int PORT = 8000; const int MAX_CLIENTS = 1024; const int BUFFER_SIZE = 1024; /**************************************************** * 主函数 ****************************************************/ int main() { // 1. 创建监听 socket int listenFd = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in serverAddr{}; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(PORT); serverAddr.sin_addr.s_addr = INADDR_ANY; bind(listenFd, (sockaddr*)&serverAddr, sizeof(serverAddr)); listen(listenFd, 5); cout << "select 服务器启动,端口:" << PORT << endl; // 2. 初始化 fd 集合 fd_set masterSet, readSet; FD_ZERO(&masterSet); FD_SET(listenFd, &masterSet); int maxFd = listenFd; // 客户端 fd 数组 int clientFds[MAX_CLIENTS]; memset(clientFds, -1, sizeof(clientFds)); // 3. 主循环 while (true) { // 每次 select 前需要拷贝集合 readSet = masterSet; int ready = select(maxFd + 1, &readSet, nullptr, nullptr, nullptr); if (ready < 0) { perror("select"); break; } // 4. 判断监听 socket if (FD_ISSET(listenFd, &readSet)) { sockaddr_in clientAddr{}; socklen_t len = sizeof(clientAddr); int connFd = accept(listenFd, (sockaddr*)&clientAddr, &len); cout << "新客户端连接:" << connFd << endl; // 保存客户端 fd for (int i = 0; i < MAX_CLIENTS; ++i) { if (clientFds[i] < 0) { clientFds[i] = connFd; break; } } FD_SET(connFd, &masterSet); if (connFd > maxFd) maxFd = connFd; } // 5. 处理客户端 socket for (int i = 0; i < MAX_CLIENTS; ++i) { int fd = clientFds[i]; if (fd < 0) continue; if (FD_ISSET(fd, &readSet)) { char buffer[BUFFER_SIZE]; memset(buffer, 0, BUFFER_SIZE); int bytes = recv(fd, buffer, BUFFER_SIZE, 0); if (bytes <= 0) { cout << "客户端断开:" << fd << endl; close(fd); FD_CLR(fd, &masterSet); clientFds[i] = -1; } else { cout << "收到数据:" << buffer << endl; // Echo 回显 send(fd, buffer, bytes, 0); } } } } close(listenFd); return 0; }

六、代码详细解读(仅解读方法作用)

  • FD_ZERO / FD_SET / FD_CLR:维护监听的文件描述符集合

  • select:阻塞等待任意 fd 就绪

  • FD_ISSET:判断某个 fd 是否就绪

  • 监听 fd:负责接收新客户端连接

  • 客户端 fd:负责接收和发送数据


七、项目详细总结

通过该项目,你已经完整掌握:

  • I/O 多路复用的核心思想

  • select 模型的完整工作流程

  • fd_set 的使用方法

  • 单线程处理多客户端的基本模式

  • Reactor 模型的最原始形态

这是从:

阻塞式网络编程 → 高并发网络编程

第一道门槛


八、项目常见问题及解答

Q1:为什么每次 select 前要拷贝 fd_set?
A:select 会修改 fd_set,只保留就绪 fd。

Q2:select 能支持多少客户端?
A:受限于 FD_SETSIZE,通常为 1024。

Q3:select 和 epoll 的本质区别?
A:select 是轮询模型,epoll 是事件通知模型。


九、扩展方向与性能优化

  1. 升级为poll 模型

  2. 升级为epoll 模型

  3. 使用非阻塞 socket

  4. 封装为 Reactor 类

  5. 引入线程池

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/26 12:25:46

从需求到架构:企业知识库AI助手的敏捷开发实践

从需求到架构:企业知识库AI助手的敏捷开发实践——以用户价值为核心的迭代式系统构建 元数据框架 标题 从需求到架构:企业知识库AI助手的敏捷开发实践——以用户价值为核心的迭代式系统构建 关键词 企业知识库、AI助手、敏捷开发、检索增强生成(RAG)、需求工程、系统架…

作者头像 李华
网站建设 2026/1/20 15:30:30

cv_unet_image-matting处理速度慢?GPU利用率提升优化教程

cv_unet_image-matting处理速度慢&#xff1f;GPU利用率提升优化教程 1. 引言&#xff1a;图像抠图性能瓶颈与优化目标 在基于 U-Net 架构的 cv_unet_image-matting 图像抠图项目中&#xff0c;尽管模型具备高精度的人像分割能力&#xff0c;但在实际使用过程中&#xff0c;用…

作者头像 李华
网站建设 2026/1/26 22:33:24

Scrapy ImagesPipeline和FilesPipeline自定义使用

Scrapy 作为 Python 生态中强大的爬虫框架&#xff0c;内置了ImagesPipeline和FilesPipeline两个核心管道&#xff0c;专门用于处理图片、文件的下载需求。默认配置虽能满足基础场景&#xff0c;但实际开发中&#xff0c;我们常需要自定义存储路径、过滤文件格式、处理下载异常…

作者头像 李华
网站建设 2026/1/25 7:00:10

Fun-ASR真实体验分享:会议录音转文字超高效

Fun-ASR真实体验分享&#xff1a;会议录音转文字超高效 在远程办公和线上协作日益普及的今天&#xff0c;会议记录已成为日常工作中不可或缺的一环。然而&#xff0c;手动整理录音不仅耗时耗力&#xff0c;还容易遗漏关键信息。有没有一种工具&#xff0c;能将会议录音快速、准…

作者头像 李华
网站建设 2026/1/19 12:19:59

Alkyne-PEG-Do;Alkyne-PEG-Dopamine:炔基 PEG 多巴胺的核心性能一览

试剂基本信息中文名称&#xff1a;丙炔聚乙二醇多巴胺&#xff1b;丙炔-聚乙二醇-多巴胺英文名称&#xff1a;Alkyne-PEG-Do&#xff1b;Dopamine-PEG-Alkyne&#xff1b;Alkyne-PEG-Dopamine外观&#xff1a;液体或固体粉末溶解性&#xff1a;溶于有机溶剂纯度&#xff1a;95%…

作者头像 李华
网站建设 2026/1/19 12:11:22

开箱即用!Docker快速部署Fun-ASR-MLT-Nano语音识别服务

开箱即用&#xff01;Docker快速部署Fun-ASR-MLT-Nano语音识别服务 1. 项目背景与技术价值 1.1 多语言语音识别的工程挑战 在跨语言交互、智能客服、会议转录等场景中&#xff0c;多语言语音识别&#xff08;Automatic Speech Recognition, ASR&#xff09;已成为关键能力。…

作者头像 李华