当前位置: 首页 > news >正文

C++网络编程之IO多路复用(一)

概述

        在C++网络编程中,处理并发连接是一个非常关键的核心问题。为了有效管理来自多个客户端的请求,服务器需要能够同时监听多个套接字上的事件,这通常通过IO多路复用来实现。

        IO多路复用是一种工作机制,它可以让程序监视多个文件描述符(通常是套接字),等待其中一个或多个文件描述符变为就绪状态。一旦某个文件描述符就绪,即该文件描述符上可以进行无阻塞读写操作,操作系统就会通知应用程序。然后,应用程序就可以对该文件描述符进行相应的读写操作。

        使用IO多路复用后,不需要为每个连接创建一个独立的线程,节省了资源。同时,也避免了频繁的上下文切换开销,提高了效率。常见的IO多路复用技术主要有三种,分别为:select、poll、epoll。在本篇中,我们将重点介绍select,后续文章将介绍poll和epoll。

select

        select是最古老的IO多路复用API,几乎支持所有类型的Unix系统(包括:Windows、Linux、Mac等)。它的基本工作原理是:用户态进程将一组文件描述符传递给内核,由内核来检查这些文件描述符的状态变化;当调用返回时,会告诉用户哪些文件描述符已经准备好了读写操作。

        select函数的接口原型如下。

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

        各个参数和返回值的含义如下。

        nfds:指定要监视的最大文件描述符值加1。

        readfds:指向一个集合,该集合包含了想要监视读就绪状态的文件描述符。

        writefds:指向一个集合,该集合包含了想要监视写就绪状态的文件描述符。

        exceptfds:指向一个集合,该集合包含了想要监视异常条件的文件描述符。

        timeout:一个时间结构体,指定了等待超时的时间。如果设置为NULL,则会一直阻塞,直到至少有一个文件描述符准备好。如果设置了具体时间,则在指定时间内没有事件发生将返回0。

        返回值:如果返回正数,这意味着至少有一个文件描述符已经准备好执行请求的操作(读、写或异常条件)。返回的具体数值,表示有多少个文件描述符已就绪。如果返回 0,这意味着在指定的超时时间内没有任何文件描述符准备好,并且超时时间已过。如果返回负数,这意味着调用过程中发生了错误。常见的原因包括传入了无效的参数或者系统本身遇到了问题。

        在Windows系统和Linux系统下使用select函数时,它们之间存在一些细微的差异,具体如下。

        1、头文件和库不同。

        Windows:需要包含winsock2.h头文件,且链接到Winsock库ws2_32.lib。

        Linux:需要包含sys/select.h头文件,不需要链接额外的库,因为select是标准POSIX库的一部分。

        2、类型不同。

        Windows:Socket类型为套接字句柄。

        Linux:Socket类型为文件描述符,这是所有IO操作的基础。

        3、FD_SETSIZE的定义不同。

        Windows:默认的FD_SETSIZE是64。

        Linux:默认的FD_SETSIZE是1024。

        4、错误码不同。

        Windows:返回SOCKET_ERROR,表示发生了错误。可通过WSAGetLastError函数获取更具体的错误值,比如:WSAEINTR(被信号中断)、WSAEINVAL(无效参数)等。

        Linux:返回-1,表示发生了错误。可通过errno获取更具体的错误值,比如:EINTR(被信号中断)、EINVAL(无效参数)等。

        注意:select只能处理有限数量的文件描述符,通常这个数量由上面介绍的FD_SETSIZE定义。当监视大量的文件描述符时,select的性能会显著下降。每次调用select都需要复制所有文件描述符集合到内核空间,然后在内核中进行线性扫描。这种机制对于少量的文件描述符是可行的,但对于大规模并发应用则效率低下。

实战代码

        在Windows系统下如何使用select进行IO多路复用,可参考下面的TCP服务器的示例代码。

        首先,我们使用WSAStartup初始化Winsock库,并检查是否成功。接着,创建一个监听套接字,将其绑定到指定的端口8888,并调用listen开始监听连接请求。

        然后,我们使用fd_set类型的变量masterSet来存储所有需要监控的套接字,包括:监听套接字、所有已连接的客户端套接字;workingSet则用于临时存储select检查的结果。

        紧接着,在无限循环中,我们使用select函数来等待IO事件的发生。如果select返回新的连接请求,接受新连接,并将新连接的套接字添加到masterSet和activeSockets向量中。对于每个已连接的客户端套接字,检查是否有数据可读。如果有,则读取数据并回显给客户端。如果客户端断开连接,则关闭相应的套接字,并从masterSet和activeSockets中移除该套接字。

        最后,当程序退出时,关闭所有打开的套接字,并清理Winsock库。

#include <iostream>
#include <winsock2.h>
#include <vector>
using namespace std;#pragma comment(lib, "ws2_32.lib")#define TCP_LISTEN_PORT                   8888
#define BUFFER_SIZE                       1024int main()
{// 初始化Winsock库WSADATA wsaData;int result = WSAStartup(MAKEWORD(2, 2), &wsaData);if (result != 0){cout << "WSAStartup failed: " << result << endl;return 1;}SOCKET listenSocket = INVALID_SOCKET;struct sockaddr_in serverAddr;// 创建监听 socketlistenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (listenSocket == INVALID_SOCKET){cout << "Socket creation failed: " << WSAGetLastError() << endl;WSACleanup();return 1;}// 设置服务器地址serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = INADDR_ANY;serverAddr.sin_port = htons(TCP_LISTEN_PORT);if (bind(listenSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR){cout << "Bind failed: " << WSAGetLastError() << endl;closesocket(listenSocket);WSACleanup();return 1;}// 开始监听if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR){cout << "Listen failed: " << WSAGetLastError() << endl;closesocket(listenSocket);WSACleanup();return 1;}cout << "Server listening on port " << TCP_LISTEN_PORT << endl;fd_set masterSet;FD_ZERO(&masterSet);FD_SET(listenSocket, &masterSet);fd_set workingSet;// 存储新连接的套接字句柄vector<SOCKET> activeSockets;activeSockets.push_back(listenSocket);while (true){workingSet = masterSet;// 调用selectint selectResult = select(0, &workingSet, NULL, NULL, NULL);if (selectResult == SOCKET_ERROR){cout << "Select failed: " << WSAGetLastError() << endl;break;}// 检查是否有新的连接请求if (FD_ISSET(listenSocket, &workingSet)){SOCKET clientSocket = accept(listenSocket, NULL, NULL);if (clientSocket == INVALID_SOCKET){cout << "Accept failed: " << WSAGetLastError() << endl;continue;}cout << "New connection: " << clientSocket << endl;FD_SET(clientSocket, &masterSet);activeSockets.push_back(clientSocket);}// 检查其他已连接的客户端是否有数据可读for (const auto &sock : activeSockets){// 跳过监听Socketif (sock == listenSocket){continue;}if (FD_ISSET(sock, &workingSet)){char buffer[BUFFER_SIZE] = { 0 };int bytesReceived = recv(sock, buffer, BUFFER_SIZE, 0);if (bytesReceived > 0){cout << "Received from client " << sock << ": " << buffer << endl;// 回显消息给客户端send(sock, buffer, bytesReceived, 0);}else if (bytesReceived == 0){cout << "Client disconnected: " << sock << endl;closesocket(sock);FD_CLR(sock, &masterSet);// 移除断开的连接auto it = find(activeSockets.begin(), activeSockets.end(), sock);if (it != activeSockets.end()){activeSockets.erase(it);}}else{cout << "Recv failed: " << WSAGetLastError() << endl;closesocket(sock);FD_CLR(sock, &masterSet);// 移除断开的连接auto it = find(activeSockets.begin(), activeSockets.end(), sock);if (it != activeSockets.end()){activeSockets.erase(it);}}}}}// 释放资源for (const auto &sock : activeSockets){closesocket(sock);}closesocket(listenSocket);WSACleanup();return 0;
}

相关文章:

C++网络编程之IO多路复用(一)

概述 在C网络编程中&#xff0c;处理并发连接是一个非常关键的核心问题。为了有效管理来自多个客户端的请求&#xff0c;服务器需要能够同时监听多个套接字上的事件&#xff0c;这通常通过IO多路复用来实现。 IO多路复用是一种工作机制&#xff0c;它可以让程序监视多个文件描述…...

vscode在windows和linux如何使用cmake构建项目并make生成可执行文件,两者有什么区别

vscode在windows和linux如何使用cmake构建项目并make生成可执行文件&#xff0c;两者有什么区别 windows默认使用的是最新的visual studio&#xff0c;而linux默认就是cmake 文章目录 vscode在windows和linux如何使用cmake构建项目并make生成可执行文件&#xff0c;两者有什么…...

Antd Vue中使用table组件把相同名称的合并单元格---只需两步

当前效果&#xff1a; 想要的效果&#xff1a; 第一步&#xff1a;在获取table数据的地方处理数据 function getTableList () {getDataList().then(res > {if (res.code 200 && res.data) {const list res.datalet columnIndex 0 //第一条数据let rowSpan …...

cmake中execute_process详解

execute_process 是 CMake 中一个非常强大的命令&#xff0c;用于在构建过程中执行外部程序或脚本。它提供了丰富的选项来控制执行过程&#xff0c;并可以捕获输出、错误和返回码。以下是 execute_process 的详细解析&#xff1a; 基本语法 execute_process(COMMAND <comm…...

搜维尔科技:使用Sensglove Nova2触觉反馈手套遥操作机器人操作

使用Sensglove Nova2触觉反馈手套遥操作机器人操作 搜维尔科技&#xff1a;使用Sensglove Nova2触觉反馈手套遥操作机器人操作...

企业HR如何选对一款智能招聘软件?

随着招聘市场的竞争加剧和求职者期望的提升&#xff0c;传统的招聘方式已经难以满足企业的需求。智能招聘软件应运而生&#xff0c;成为企业HR提升招聘效率、优化招聘流程、增强雇主品牌吸引力的关键工具。然而&#xff0c;市场上的智能招聘软件琳琅满目&#xff0c;如何选择一…...

任务中心全新升级,新增分享接口文档功能,MeterSphere开源持续测试工具v3.4版本发布

2024年11月5日&#xff0c;MeterSphere开源持续测试工具正式发布v3.4版本。 在这一版本中&#xff0c;系统设置方面&#xff0c;任务中心支持实时查看系统即时任务与系统后台任务&#xff1b;接口测试方面&#xff0c;新增接口文档分享功能、接口场景导入导出功能&#xff0c;…...

书生大模型第三关Git 基础知识

关卡编号&#xff1a;L0G3000 任务一 破冰行动 fork仓库&#xff0c;注意这里不要勾选Copy branch Only!!!&#xff0c;因为后面课程中会使用到class分支&#xff1a; 克隆仓库&#xff1a; 移动分支&#xff1a; 创建自己的分支&#xff1a; 创建id.md文档&#xff0c;…...

WordPress 中最佳的维护服务:入门级用户指南

如果你是WordPress网站管理员&#xff0c;一定知道网站维护既耗时又复杂。然而&#xff0c;保持网站的正常运行和安全却至关重要。为了让你轻松应对这个挑战&#xff0c;我们总结了一些适合新手和小型网站的维护服务。本文将介绍两款适合初学者的维护服务&#xff1a;FixMySite…...

前端使用Luckysheet把返回的base64或二进制文件流格式,实现xlsx文件预览

xlsx文件预览 Luckysheet是什么&#xff1f;代码实现xlsx文件预览引入luckysheet的相关依赖安装luckyexcel指定一个表格容器实现逻辑 Luckysheet是什么&#xff1f; Luckysheet &#xff0c;一款纯前端类似excel的在线表格&#xff0c;功能强大、配置简单、完全开源。 Luckys…...

腾讯混元宣布大语言模型和3D模型正式开源

腾讯混元大模型正在加快开源步伐。 11月5日&#xff0c;腾讯混元宣布最新的MoE模型“混元Large“以及混元3D生成大模型“ Hunyuan3D-1.0”正式开源&#xff0c;支持企业及开发者精调、部署等不同场景的使用需求&#xff0c;可在HuggingFace、Github等技术社区直接下载&#xff…...

提示工程指南 笔记

诸神缄默不语-个人CSDN博文目录 课程网站&#xff1a;提示工程指南 | Prompt Engineering Guide 原版是英文&#xff1a;https://www.promptingguide.ai/ 特别基础的内容我就不写了&#xff0c;只写一些值得记录的内容。 文章目录 1. 常用术语&#xff08;LLM特供版&#xff…...

WordPress站点网站名称、logo设置

WordPress网站名称设置 后台打开查看站点自定义设置 点击网站名称修改 上传logo和站点图标...

本地缓存与 Redis:为什么我们仍然需要本地缓存?

文章目录 本地缓存与 Redis&#xff1a;为何仍需本地缓存&#xff1f;为什么需要本地缓存&#xff1f;多级缓存架构多级缓存的实现 本地缓存的实现方式使用 cachetools 实现 LRUCache使用 diskcache 实现持久化缓存 缓存装饰器实现进一步优化&#xff1a;缓存失效与更新 小结 好…...

要在微信小程序中让一个 `view` 元素内部的文字水平垂直居中,可以使用 Flexbox 布局

文章目录 主要特点&#xff1a;基本用法&#xff1a;常用属性&#xff1a; 要在微信小程序中让一个 view 元素内部的文字水平垂直居中&#xff0c;可以使用 Flexbox 布局。以下是如何设置样式的示例&#xff1a; .scan-button {display: flex; /* 启用 Flexbox 布局 */justify…...

图像超分辨率、DPSRGAN

图像超分辨率&#xff08;Image Super-Resolution, ISR&#xff09;是一种通过增加图像的分辨率来提高其细节和清晰度的技术。这项技术在多个领域都有广泛的应用&#xff0c;比如视频监控、医学诊断、遥感应用等。根据搜索结果&#xff0c;图像超分辨率算法主要可以分为以下几类…...

124.WEB渗透测试-信息收集-ARL(15)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;123.WEB渗透测试-信息收集-ARL&#xff08;14&#xff09; 点击fofa任务下发&#xff08…...

@Async注解提升Spring Boot项目中API接口并发能力

文章目录 同步调用异步调用1: 启用异步支持2: 修改 Task 类异步回调基本概念使用 Future<String>使用 CompletableFuture<String>Future<String> 和 CompletableFuture<String>区别1. 基本概念2. 主要区别同步调用 同步调用是最直接的调用方式,调用方…...

SpringBoot集成Flink-CDC

Flink CDC CDC相关介绍 CDC是什么? CDC是Change Data Capture(变更数据获取)的简称。核心思想是&#xff0c;监测并捕获数据库的变动&#xff08;包括数据或数据表的插入、更新以及删除等&#xff09;&#xff0c;将这些变更按发生的顺序完整记录下来&#xff0c;写入到MQ以…...

SQL报错注入检测方法与攻击方法

报错注入 即是注入检测方法&#xff0c;又是注入读取数据的方法 攻击者在判断一个参数是否存在SQL注入漏洞时&#xff0c;会拼接单引号&#xff0c;反斜杠字符&#xff0c;如果显示语法报错&#xff0c;证明这个位置具有SQL注入漏洞&#xff0c;也可以通过整数溢出来判断&…...

数据类型与变量-Part3-输入输出格式化艺术

C语言输入输出格式化艺术系列导航 ✅ Part 1: C语言数据类型与变量&#xff08;基础篇&#xff09;✅ Part 2: C语言内存探秘&#xff08;进阶篇&#xff09;&#x1f4cd; Part 3: C语言输入输出格式化艺术 ← 你在这里上一篇我们深入了内存底层&#xff0c;这篇我们来聊聊你和…...

XZ62N,0.7uA静态电流,NMOS输出电压检测芯片

产品概述 这系列芯片是使用 CMOS 技术开发的高精度、低功耗、小封装电压检测芯片。检测电压在小温度漂移的情况下保持极高的精度。输出配置是N-channel open drai 输出。 产品特点 ● 封装&#xff1a;SOT23-3 ● 输出配置&#xff1a;N-channel open drain ● 工作电压&a…...

Unity风格化山脉系统:程序化生成与运行时自然逻辑

1. 这不是“又一个山地素材包”&#xff0c;而是一套可工业化复用的风格化自然系统你有没有在Unity项目里&#xff0c;拖进一个山体模型&#xff0c;调完材质发现它和场景里其他植被、岩石、雾效完全不搭&#xff1f;或者好不容易调出理想中的晨雾山色&#xff0c;换到另一个光…...

ProperTree:重新定义Plist编辑的技术哲学与设计范式

ProperTree&#xff1a;重新定义Plist编辑的技术哲学与设计范式 【免费下载链接】ProperTree Cross platform GUI plist editor written in python. 项目地址: https://gitcode.com/gh_mirrors/pr/ProperTree 在macOS和iOS开发的世界里&#xff0c;Property List&#x…...

5分钟完成Windows 11终极优化:开源神器Win11Debloat完全指南

5分钟完成Windows 11终极优化&#xff1a;开源神器Win11Debloat完全指南 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutter …...

构建AI模型实时反馈回路:从概念漂移到持续进化

1. 项目概述&#xff1a;当AI模型不再“一锤定音”&#xff0c;而是持续呼吸、自我校准你有没有遇到过这样的情况&#xff1a;一个花了三个月调优的推荐模型&#xff0c;上线首周点击率提升12%&#xff0c;第二周开始缓慢下滑&#xff0c;到第四周几乎回到基线水平&#xff1f;…...

【Java并发编程】Java虚拟线程与平台线程的区别、虚拟线程调度、适用/不适用场景、在Spring Boot中的集成(2026高频)(附《思维导图》+《面试高频考点清单》)

文章目录Java并发编程&#xff1a;虚拟线程系统性知识体系&#xff08;2026高频&#xff09;一、虚拟线程概述与发展历程1.1 核心定义1.2 发展里程碑1.3 核心价值二、虚拟线程与平台线程的核心区别2.1 本质差异对比表2.2 关键差异详细解释2.2.1 内存模型差异2.2.2 阻塞处理机制…...

暗黑破坏神2存档修改完全指南:免费工具5分钟打造完美角色

暗黑破坏神2存档修改完全指南&#xff1a;免费工具5分钟打造完美角色 【免费下载链接】d2s-editor 项目地址: https://gitcode.com/gh_mirrors/d2/d2s-editor 你是否曾在《暗黑破坏神2》中因为技能点加错而懊恼不已&#xff1f;是否因为稀有装备刷了上百小时仍未掉落而…...

SMUDebugTool深度解析:AMD Ryzen处理器硬件调试技术指南

SMUDebugTool深度解析&#xff1a;AMD Ryzen处理器硬件调试技术指南 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://…...

DKC02.3-200-7-FW伺服驱动器

Rexroth DKC02.3-200-7-FW 是博世力士乐 Indramat 系列的高性能数字伺服驱动器&#xff0c;专为高动态响应的工业自动化场景设计。大电流输出&#xff1a;额定100A&#xff0c;峰值200A&#xff0c;满足高负载需求。宽压输入&#xff1a;支持200-480V AC&#xff0c;适应全球电…...