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

【计算机网络】非阻塞IO——poll实现多路转接

🔥个人主页🔥:孤寂大仙V
🌈收录专栏🌈:计算机网络
🌹往期回顾🌹:【计算机网络】非阻塞IO——select实现多路转接
🔖流水不争,争的是滔滔不息


一、poll实现多路转接

在网络编程或多路 IO 编程中,我们经常需要同时监听多个文件描述符(fd),比如多个客户端的 socket 连接。这时,poll 就登场了。
poll 是 Linux 提供的一种 IO 多路复用机制,用于监听多个 fd 上的读写等事件,一旦有就绪,立刻通知我们处理。

poll的参数

int poll(struct pollfd fds[], nfds_t nfds, int timeout);
  1. 参数struct pollfd fds[]是关心的文件描述符数组,每一个元素代表一个要监听的 fd 及其感兴趣的事件和返回的事件。
struct pollfd {int fd;         // 要监听的文件描述符short events;   // 感兴趣的事件(由你设置)short revents;  // 实际返回的事件(由内核设置)
};
  1. 参数 nfds_t nfds这个是 fds[] 数组里有效元素的数量,简单说就是你监听几个文件描述符就写几。
  2. int timeout
    单位是 毫秒(ms),表示阻塞多久。timeout > 0:等待指定毫秒数后返回、timeout == 0:立即返回(非阻塞)、timeout < 0:永远阻塞,直到有事件发生。

函数返回值

  • 0:就绪的文件描述符数量
  • 0:超时,没有任何事件发生
  • <0:出错(比如信号中断)

常用事件(events/revents)取值

POLLIN     // 有数据可读
POLLOUT    // 可以写数据而不会阻塞
POLLERR    // 错误(由 revents 设置)
POLLHUP    // 对端关闭连接
POLLNVAL   // 描述符非法

poll的缺点

  1. 每次调用都要传入整个 fd 列表
    poll 的 pollfd 是个数组,内核不会保存状态,每次都得重新传。
    如果你有上千个连接,那每次 poll() 都会把这上千个 fd 重新复制到内核 → 代价大。
  2. 线性扫描效率低
    poll 返回的是“多少个 fd 就绪”,但你要线性扫描整个数组找出来。
    比如你监听 1000 个连接,但只有 1 个可读,你得从 0 扫到 999 才找到它。
  3. fd 数量仍有限制
    虽然 poll 相比 select 不再限制 1024 个,但:
    它还是受限于内核最大文件描述符数量(ulimit -n,比如 65535)
    每个 pollfd 占用空间较大,更不适合极大并发
  4. 无事件通知机制
    poll 只能“等和扫”,没有像 epoll 的 EPOLLONESHOT、EPOLLET(边缘触发)那种高级机制。
    没法在事件处理完后说“我暂时不关注这个 fd 了”。

二、poll实现非阻塞服务器

#pragma once
#include "Common.hpp"
#include "Log.hpp"
#include "Socket.hpp"
#include <sys/poll.h>using namespace std;
using namespace LogModule;
using namespace SocketModule;class PollServer
{const static int size = 4096;const static int defaultfd = -1;public:PollServer(int port): _isrunning(false), _listensockfd(make_unique<TcpSocket>()){_listensockfd->BuildTcpSocketServer(port); // 构造TCP服务器for (int i = 0; i < size; i++){_fds[i].fd = defaultfd;_fds[i].events = 0;_fds[i].revents = 0;}_fds[0].fd = _listensockfd->FD();_fds[0].events = POLLIN;}void Start() // 服务器启动{int timeout = 1000; // 1000毫秒_isrunning = true;while (true){int n = poll(_fds, size, timeout); // 多路转接只关系读事件switch (n){case -1:LOG(LogLevel::ERROR) << "poll error"; // 异常break;case 0:LOG(LogLevel::WARNING) << "poll timeout"; // 超时default:LOG(LogLevel::INFO) << "事件就绪"; // 读事件就绪Dispatcher();                      // 派发break;}}}void Dispatcher() // 事件派发{for (int i = 0; i < size; i++){if (_fds[i].fd == defaultfd) // 跳过continue;if (_fds[i].revents & POLLIN) // 是读就绪{if (_fds[i].fd == _listensockfd->FD()){// listen 套接字Accept();}else{// 普通  套接字Recv(i);}}}}void Accept(){InetAddr client;int sockfd = _listensockfd->AcceptOrDie(&client);LOG(LogLevel::DEBUG) << "accept a new client" << client.StringAddr();int pos = 0;for (; pos < size; pos++){if (_fds[pos].fd == defaultfd)break; // 数组中找到空位}if (pos == size){LOG(LogLevel::WARNING) << "poll server full";close(sockfd);}else{_fds[pos].fd = sockfd;_fds[pos].events = POLLIN;_fds[pos].revents = 0;}}void Recv(int pos) // 读数据{char buffer[1024];ssize_t n = recv(_fds[pos].fd, buffer, sizeof(buffer) - 1, 0); // 收信息if (n > 0){buffer[n] = 0;cout << "client say@ " << buffer << endl;}else if (n == 0) // 客户端退出{LOG(LogLevel::INFO) << "client quit";_fds[pos].fd = defaultfd;_fds[pos].events = 0;_fds[pos].revents = 0;close(_fds[pos].fd);}else // 出现错误 异常{LOG(LogLevel::FATAL) << "recv error";_fds[pos].fd = defaultfd;_fds[pos].events = 0;_fds[pos].revents = 0;close(_fds[pos].fd);}}~PollServer(){}private:unique_ptr<Socket> _listensockfd;bool _isrunning;struct pollfd _fds[size];
};

构造

//私有成员变量
private:unique_ptr<Socket> _listensockfd;bool _isrunning;struct pollfd _fds[size];
//构造PollServer(int port): _isrunning(false), _listensockfd(make_unique<TcpSocket>()){_listensockfd->BuildTcpSocketServer(port); // 构造TCP服务器for (int i = 0; i < size; i++){_fds[i].fd = defaultfd;_fds[i].events = 0;_fds[i].revents = 0;}_fds[0].fd = _listensockfd->FD();_fds[0].events = POLLIN;}

私有成员变量中,把关心文件描述符的数组开好,构造服务器的时候遍历这个数组,把要监听的文件描述符设置进去,把要关系的状态设置为关系读事件。


服务器启动

void Start() // 服务器启动{int timeout = 1000; // 1000毫秒_isrunning = true;while (true){int n = poll(_fds, size, timeout); // 多路转接只关系读事件switch (n){case -1:LOG(LogLevel::ERROR) << "poll error"; // 异常break;case 0:LOG(LogLevel::WARNING) << "poll timeout"; // 超时default:LOG(LogLevel::INFO) << "事件就绪"; // 读事件就绪Dispatcher();                      // 派发break;}}}

用poll函数,进行多路转接,事件就绪就派发任务。


事件派发

void Dispatcher() // 事件派发{for (int i = 0; i < size; i++){if (_fds[i].fd == defaultfd) // 跳过continue;if (_fds[i].revents & POLLIN) // 是读就绪{if (_fds[i].fd == _listensockfd->FD()){// listen 套接字Accept();}else{// 普通  套接字Recv(i);}}}}

对文件描述符数组,进行遍历。如果不是合法位置(没放文件描述符)就跳过这个位置。如果是读就绪然后判断是监听套接字合适普通套接字。

收到客户端的连接accept

void Accept(){InetAddr client;int sockfd = _listensockfd->AcceptOrDie(&client);LOG(LogLevel::DEBUG) << "accept a new client" << client.StringAddr();int pos = 0;for (; pos < size; pos++){if (_fds[pos].fd == defaultfd)break; // 数组中找到空位}if (pos == size){LOG(LogLevel::WARNING) << "poll server full";close(sockfd);}else{_fds[pos].fd = sockfd;_fds[pos].events = POLLIN;_fds[pos].revents = 0;}}

主要是收到客户端的连接,创建了accept的套接字,要把这个套接字放到数组中。在文件描述符数组中找到空位,把这个acceptfd设置进去。


读数据

void Recv(int pos) // 读数据{char buffer[1024];ssize_t n = recv(_fds[pos].fd, buffer, sizeof(buffer) - 1, 0); // 收信息if (n > 0){buffer[n] = 0;cout << "client say@ " << buffer << endl;}else if (n == 0) // 客户端退出{LOG(LogLevel::INFO) << "client quit";_fds[pos].fd = defaultfd;_fds[pos].events = 0;_fds[pos].revents = 0;close(_fds[pos].fd);}else // 出现错误 异常{LOG(LogLevel::FATAL) << "recv error";_fds[pos].fd = defaultfd;_fds[pos].events = 0;_fds[pos].revents = 0;close(_fds[pos].fd);}

这时候,读数据已经是非阻塞的了,客户端退出和异常要把文件描述符数组的内容清空,然后关闭文件描述符。


在这里插入图片描述
源码:poll多路转接

相关文章:

【计算机网络】非阻塞IO——poll实现多路转接

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;计算机网络 &#x1f339;往期回顾&#x1f339;&#xff1a;【计算机网络】非阻塞IO——select实现多路转接 &#x1f516;流水不争&#xff0c;争的是滔滔不息 一、…...

在.NET Core控制器中获取AJAX传递的Body参数

.Net Core是支持前后端不分离式的开发的&#xff0c;如果在原始系统中采用不分离式开发&#xff0c;后面需要在原系统中增加功能&#xff0c;并且新的服务采用其他语言开发&#xff0c;且系统原来功能保持原样&#xff0c;这样前端系统可以单独调用新开发的接口。 但是&#x…...

snprintf函数用法及注意事项详解

当 format 后没有可变参数&#xff08;即 ... 为空&#xff09;时&#xff0c;va_start 的行为和后续操作如下&#xff1a; 1. va_start 的行为 va_start 的核心任务是根据最后一个固定参数&#xff08;format&#xff09;的地址&#xff0c;计算可变参数列表的起始位置。即使…...

vue-20(Vuex 状态管理的最佳实践)

Vuex 状态管理的最佳实践 Vuex 是管理大型 Vue.js 应用状态的一个强大工具&#xff0c;但其有效性取决于其组织和维护的质量。管理不善的 Vuex 存储可能会变得难以控制、难以调试&#xff0c;并成为性能瓶颈。本课程深入探讨构建 Vuex 存储的最佳实践&#xff0c;重点关注可维…...

DAX权威指南8:DAX引擎与存储优化

文章目录 十七、DAX引擎17.1 DAX 引擎的体系结构17.1.1 表格模型的双引擎架构17.1.2 存储引擎的三种模式17.1.2.1 VertiPaq引擎17.1.2.2 DirectQuery 引擎17.1.2.3 对比与最佳实践 17.1.3 数据刷新 17.2 理解 VertiPaq 存储引擎17.2.1 列式数据库17.2.2 VertiPaq 压缩17.2.2.1 …...

智慧货运飞船多维度可视化管控系统

图扑搭建智慧货运飞船可视化系统&#xff0c;借数字孪生技术&#xff0c;高精度复刻货运飞船外观、结构与运行场景。整合多维度数据&#xff0c;实时呈现飞行状态、设备参数等信息&#xff0c;助力直观洞察货运飞船运行逻辑&#xff0c;为航天运维、任务推演及决策提供数字化支…...

电脑开不了机,主板显示67码解决过程

文章目录 现象分析内存条问题BIOS设置问题其它问题 解决清理内存条金手指所需工具操作步骤注意事项 电脑在运行过程中&#xff0c;显示内存不足&#xff0c;重启电脑却无法启动。 现象 System Initialization 主板风扇是转的&#xff0c;也有灯光显示&#xff0c;插上屏幕&am…...

Spring Boot 类加载机制深度解析

Spring Boot 类加载机制深度解析 前言 在 Java 应用开发中&#xff0c;类加载机制是一个重要且复杂的话题。Spring Boot 作为现代 Java 开发的主流框架&#xff0c;其类加载机制更是值得深入了解。本文将从基础概念到实际应用&#xff0c;全面解析 Spring Boot 的类加载机制。…...

Python 训练营打卡 Day 45

TensorBoard 简单来说&#xff0c;TensorBoard 是 TensorFlow 自带的一个「可视化工具」&#xff0c;就像给机器学习模型训练过程装了一个「监控屏幕」。你可以用它直观看到训练过程中的数据变化&#xff08;比如损失值、准确率&#xff09;、模型结构、数据分布等&#xff0c…...

自托管图书搜索引擎Bookologia

简介 什么是 Bookologia &#xff1f; Bookologia 是一个专门的书籍搜索引擎&#xff0c;可以在几秒钟内找到任何书籍。它是开源的&#xff0c;可以轻松自托管在 Docker 上&#xff0c;为用户提供一个简单而高效的书籍查找体验。 主要特点 简洁的用户界面&#xff1a;界面设计…...

前端flex、grid布局

flex布局 弹性布局是指通过调整其内元素的宽高&#xff0c;从而在任何的显示设备上实现对可用显示空间最佳填充的能力。弹性容器扩展其内元素来填充可用空间&#xff0c;或将其收缩来避免溢出 简单来说&#xff0c;弹性盒子模型&#xff0c;是为了你的网页可以在不同分辨率设…...

Maven相关问题:jna版本与ES冲突 + aop失效

文章目录 1、背景2、解决3、一点思考4、环境升级导致AOP失效5、okhttp Bean找不到6、总结 记录一些Maven依赖相关的思考 1、背景 做一个监控指标收集&#xff0c;用一下jna依赖&#xff1a; <dependency><groupId>net.java.dev.jna</groupId><artifact…...

Tomcat全方位监控实施方案指南

#作者&#xff1a;程宏斌 文章目录 一&#xff0e;二进制部署1、安装包信息2、新建配置文件2.1 配置config.yaml文件2.2 上传jar包 3、修改配置3.1 备份3.2 修改bin目录下的startup.sh文件 4、重启tomcat5、访问测试 二&#xff0e;docker部署1、临时方案1.1、重新启动容器1.2…...

开源PHP在线客服系统源码搭建教程

在当今数字化时代&#xff0c;在线客服系统已成为企业与客户沟通的重要桥梁。开源PHP客服系统因其灵活性、低成本和高可定制性而受到众多企业的青睐。本文将介绍几款优秀的开源PHP客服系统&#xff0c;并提供详细的搭建教程。 演示网站&#xff1a;gofly.v1kf.com 1.1 主流开源…...

centos7升级glibic-2.28

centos7升级glibic-2.28 最近使用trae连接服务器的时候&#xff0c;提示远程系统不兼容: Trae CN需要glibc 2.28或更高版本。检测到的版本: 2.17。下面是升级步骤。centos7默认的glibc不支持node v18及以上。 1、进入/home/download目录(没有download&#xff0c;则新建一个)…...

在Docker里面运行Docker

Docker 凭借其轻量级和可移植的容器,无疑改变了软件开发和部署的世界。但如果我告诉你 Docker 本身可以在另一个 Docker 容器中运行,你会怎么想?没错!这个概念通常被称为“Docker Inside Docker”或“DinD”,它为开发人员和系统管理员开辟了一个全新的可能性领域。在这篇博…...

设计模式复习小结

1.容易忘得设计原则 接口隔离&#xff1a;指接口中的功能太杂则可以拆分一下。防止实现类实现了接口后自动依赖了一些不需要的功能。不同功能拆分成不同的接口。 里氏代换&#xff1a;强调父类能出现的地方&#xff0c;子类一定能正常跑。 迪米特法则&#xff1a;又称最少知…...

To be or Not to be, That‘s a Token——论文阅读笔记——Beyond the 80/20 Rule和R2R

本周又在同一方向上刷到两篇文章&#xff0c;可以说&#xff0c;……同学们确实卷啊&#xff0c;要不卷卷开放场域的推理呢&#xff1f; 这两篇都在讲&#xff1a;如何巧妙的利用带有分支能力的token来提高推理性能或效率的。 第一篇叫 Beyond the 80/20 Rule: High-Entropy Mi…...

【基础】每天掌握一个Linux命令 - awk

目录 【基础】每天掌握一个Linux命令 - awk一、工具概述二、安装方式Ubuntu/Debian系统&#xff1a;CentOS/RHEL系统&#xff1a;macOS系统&#xff1a; 三、核心功能四、基础用法基本语法常用选项内置变量基本操作示例1. 打印文件所有内容2. 打印每行的第一个字段3. 指定分隔符…...

《UE5_C++多人TPS完整教程》学习笔记37 ——《P38 变量复制(Variable Replication)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P38 变量复制&#xff08;Variable Replication&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09…...

AWS API Gateway配置日志

问题 访问API Gateway接口出现了403问题&#xff0c;具体报错如下&#xff1a; {"message":"Missing Authentication Token"}需要配置AWS API Gateway日志&#xff0c;看请求过程是什么样子的。 API Gateway 先找到API Gateway的的日志角色&#xff0c…...

Towards Open World Object Detection概述(论文)

论文&#xff1a;https://arxiv.org/abs/2103.02603 代码&#xff1a;https://github.com/JosephKJ/OWOD Towards Open World Object Detection 迈向开放世界目标检测 Abstract 摘要 Humans have a natural instinct to identify unknown object instances in their environ…...

轻松备份和恢复 Android 系统 | 4 种解决方案

我们通常会在 Android 手机上存储大量重要的个人数据&#xff0c;包括照片、视频、联系人、信息等等。如果您不想丢失宝贵的数据&#xff0c;可以备份 Android 数据。当您需要访问和使用这些数据时&#xff0c;可以将其恢复到 Android 设备。如果您想了解 Android 备份和恢复&a…...

具备强大的数据处理和分析能力的智慧地产开源了

智慧地产视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。 AI是新形势下数…...

RK3588和FPGA桥片之间IO电平信号概率性不能通信原因

1.GPIO管脚配置问题 RK3588对IO进行配置的时候&#xff0c;如果配置为多功能复用&#xff0c;没有明确IO功能&#xff0c;可能引起信号接收不稳定&#xff0c; 需要在驱动中设备树中配置管脚为GPIO功能&#xff0c;确保没有功能复用的干扰。 2.上下拉电阻阻值设置不当 GPIO引脚…...

【iSAQB软件架构】软件架构中构建块的视图:黑箱、灰箱和白箱及其交互机制

在软件架构描述中&#xff0c;黑箱视图&#xff08;Black-box&#xff09;、灰箱视图&#xff08;Gray-box&#xff09;和白箱视图&#xff08;White-box&#xff09; 是不同抽象层级的构建模块表示方式&#xff0c;用于满足不同受众和设计阶段的需求。以下是基于ISAQB标准的清…...

.net jwt实现

.NET 中实现 JWT 认证&#xff1a;详细指南 在现代的 Web 应用开发中&#xff0c;安全认证是至关重要的一环。JSON Web Token&#xff08;JWT&#xff09;作为一种广泛使用的认证机制&#xff0c;为 API 提供了安全、便捷的身份验证方式。本文将详细介绍如何在 ASP.NET Core 项…...

LangChain【7】之工具创建和错误处理策略

文章目录 一 LangChain 自定义工具概述二创建自定义工具的三种方法2.1 方法一&#xff1a;tool 装饰器2.1.1 同步方法案例2.1.2 工具描述方式1&#xff1a;传参2.1.3 工具描述方式2&#xff1a;文档字符串 2.2 方法二&#xff1a;StructuredTool类2.2.1 StructuredTool创建自定…...

如何在电脑上轻松访问 iPhone 文件

我需要将 iPhone 下载文件夹中的文件传输到 Windows 11 电脑上。我该怎么做&#xff1f;我可以在 Windows 11 上访问 iPhone 下载吗&#xff1f; 由于 iOS 和 Windows 系统之间的差异&#xff0c;在 PC 上访问 iPhone 文件似乎颇具挑战性。然而&#xff0c;只要使用正确的工具…...

Eureka REST 相关接口

可供非 Java 应用程序使用的 Eureka REST 操作。 appID 是应用程序的名称&#xff0c;instanceID 是与实例关联的唯一标识符。在 AWS 云中&#xff0c;instanceID 是实例的实例 ID&#xff1b;在其他数据中心&#xff0c;它是实例的主机名。 对于 XML/JSON&#xff0c;HTTP 的…...