Linux多路转接之poll
文章目录
- 一、poll的认识
- 二、编写poll方案服务器
- 三、poll方案多路转接的总结
一、poll的认识
多路转接技术是在不断更新进步的,一开始多路转接采用的是select方案,但是select方案存在的缺点比较多,所以在此基础上改进,产生了poll方案。poll是多路转接的另一种方案,它使用起来比select方案简单很多,也比较好用。
poll函数原型:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
select方案的最大缺点就在于它的输入和输出都是用同一个参数,比如我们将设置好的需要等待的文件描述符集合输入进去使用的是readfds这个参数,它将文件描述符集合中读事件就绪的文件描述符输出出来也是使用readfds这个参数。这就导致我们每一次都需要重置参数,所以这给我们使用select增加了很大的成本。并且fd_set是位图结构,所以就导致select函数可以检测的文件描述符数量是有上限的。
针对select方案的上述缺点,poll进行了改进:poll的参数struct pollfd *fds
其实是一个结构体数组,它里面的结构如下图所示:
其中fd表示要等待的文件描述符是什么,events表示我们要传递进去的等待事件是什么,revents表示内核给我们传递出来的事件。这个结构和select就有很大的差别,它把原来select的readfds拆分成了两个参数,将输入和输出进行了分离。第二个参数nfds_t nfds
其实就是一个整数,代表结构体数组的元素个数。第三个参数int timeout
代表等待时间,当该参数设置为-1时代表永久阻塞,当该参数设置为0时代表非阻塞,当该参数设置大于0时代表在规定时间内阻塞,超时之后返回0。
pollfd结构体中的events和revents可以设置不同的事件,它们常用的取值有:
二、编写poll方案服务器
我们可以编写一个poll方案的多路转接服务器,来演示一下poll函数接口的使用:
Sock.hpp:
#pragma once#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <cerrno>
#include <cassert>class Sock
{
public:static const int gbacklog = 20;static int Socket(){int listenSock = socket(PF_INET, SOCK_STREAM, 0);if (listenSock < 0){exit(1);}int opt = 1;setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));return listenSock;}static void Bind(int socket, uint16_t port){struct sockaddr_in local; // 用户栈memset(&local, 0, sizeof local);local.sin_family = PF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = INADDR_ANY;// 2.2 本地socket信息,写入sock_对应的内核区域if (bind(socket, (const struct sockaddr *)&local, sizeof local) < 0){exit(2);}}static void Listen(int socket){if (listen(socket, gbacklog) < 0){exit(3);}}static int Accept(int socket, std::string *clientip, uint16_t *clientport){struct sockaddr_in peer;socklen_t len = sizeof(peer);int serviceSock = accept(socket, (struct sockaddr *)&peer, &len);if (serviceSock < 0){// 获取链接失败return -1;}if(clientport) *clientport = ntohs(peer.sin_port);if(clientip) *clientip = inet_ntoa(peer.sin_addr);return serviceSock;}
};
PollServer.cc:
#include <iostream>
#include <poll.h>
#include "Sock.hpp"#define NUM 1024
struct pollfd fdsArray[NUM]; // 保存历史上所有的合法fd#define DFL -1using namespace std;static void showArray(struct pollfd arr[], int num)
{cout << "当前合法sock list# ";for (int i = 0; i < num; i++){if (arr[i].fd == DFL)continue;elsecout << arr[i].fd << " ";}cout << endl;
}static void usage(std::string process)
{cerr << "\nUsage: " << process << " port\n"<< endl;
}
// readfds: 现在包含就是已经就绪的sock
static void HandlerEvent(int listensock)
{for (int i = 0; i < NUM; i++){if (fdsArray[i].fd == DFL)continue;if (i == 0 && fdsArray[i].fd == listensock){// 我们是如何得知哪些fd,上面的事件就绪呢?if (fdsArray[i].revents & POLLIN){// 具有了一个新链接cout << "已经有一个新链接到来了,需要进行获取(读取/拷贝)了" << endl;string clientip;uint16_t clientport = 0;int sock = Sock::Accept(listensock, &clientip, &clientport); // 不会阻塞if (sock < 0)return;cout << "获取新连接成功: " << clientip << ":" << clientport << " | sock: " << sock << endl;// read/write -- 不能,因为你read不知道底层数据是否就绪!!select知道!// 想办法把新的fd托管给select?如何托管??int i = 0;for (; i < NUM; i++){if (fdsArray[i].fd == DFL)break;}if (i == NUM){cerr << "我的服务器已经到了最大的上限了,无法在承载更多同时保持的连接了" << endl;close(sock);}else{fdsArray[i].fd = sock; // 将sock添加到select中,进行进一步的监听就绪事件了!fdsArray[i].events = POLLIN;fdsArray[i].revents = 0;showArray(fdsArray, NUM);}}} // end if (i == 0 && fdsArray[i] == listensock)else{// 处理普通sock的IO事件!if(fdsArray[i].revents & POLLIN){// 一定是一个合法的普通的IO类sock就绪了// read/recv读取即可// TODO bugchar buffer[1024];ssize_t s = recv(fdsArray[i].fd, buffer, sizeof(buffer), 0); // 不会阻塞if(s > 0){buffer[s] = 0;cout << "client[" << fdsArray[i].fd << "]# " << buffer << endl; }else if(s == 0){cout << "client[" << fdsArray[i].fd << "] quit, server close " << fdsArray[i].fd << endl;close(fdsArray[i].fd);fdsArray[i].fd = DFL; // 去除对该文件描述符的select事件监听fdsArray[i].events = 0;fdsArray[i].revents = 0;showArray(fdsArray, NUM);}else{cout << "client[" << fdsArray[i].fd << "] quit, server error " << fdsArray[i].fd << endl;close(fdsArray[i].fd);fdsArray[i].fd = DFL; // 去除对该文件描述符的select事件监听fdsArray[i].events = 0;fdsArray[i].revents = 0;showArray(fdsArray, NUM);}}}}
}// ./SelectServer 8080
// 只关心读事件
int main(int argc, char *argv[])
{if (argc != 2){usage(argv[0]);exit(1);}// 是一种类型,位图类型,能定义变量,那么就一定有大小,就一定有上限// fd_set fds; // fd_set是用位图表示多个fd的// cout << sizeof(fds) * 8 << endl;int listensock = Sock::Socket();Sock::Bind(listensock, atoi(argv[1]));Sock::Listen(listensock);for (int i = 0; i < NUM; i++){fdsArray[i].fd = DFL;fdsArray[i].events = 0;fdsArray[i].revents = 0;}fdsArray[0].fd = listensock;fdsArray[0].events = POLLIN;int timeout = -1;while (true){int n = poll(fdsArray, NUM, timeout);switch (n){case 0:cout << "time out ... : " << (unsigned long)time(nullptr) << endl;break;case -1:cerr << errno << " : " << strerror(errno) << endl;break;default:HandlerEvent(listensock);// 等待成功// 1. 刚启动的时候,只有一个fd,listensock// 2. server 运行的时候,sock才会慢慢变多// 3. select 使用位图,采用输出输出型参数的方式,来进行 内核<->用户 信息的传递, 每一次调用select,都需要对历史数据和sock进行重新设置!!!// 4. listensock,永远都要被设置进readfds中!// 5. select 就绪的时候,可能是listen 就绪,也可能是普通的IO sock就绪啦!!break;}}return 0;
}
三、poll方案多路转接的总结
poll方案的优点:
poll方案的多路转接不同于select方案的多路转接使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针来实现,这个指针其实是一个结构体数组。
pollfd结构体里包含了要监视的event和发送的event,不再使用select的那种输入输出采用同一个参数的方式,因此poll函数接口使用起来比select要简单方便。
除此之外,poll方案并没有最大的数量限制,pollfd这个结构体数组的元素个数是由我们用户自己决定的,它不像select那样采用位图结构,规定了最大数量限制就是1024。但是poll方案数量过大之后性能也是会下降的。
poll方案的缺点:
poll函数和select函数一样,poll函数返回后都需要轮询检测pollfd来获取就绪的文件描述符,当pollfd中监听的文件描述符数目增多时,性能也会下降。
每次调用poll函数都需要把大量的pollfd结构从用户态拷贝到内核态,这也会因为pollfd结构数组中元素个数过多时导致性能下降。
同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的文件描述符数量的增长,其效率也会线性下降。
相关文章:

Linux多路转接之poll
文章目录 一、poll的认识二、编写poll方案服务器三、poll方案多路转接的总结 一、poll的认识 多路转接技术是在不断更新进步的,一开始多路转接采用的是select方案,但是select方案存在的缺点比较多,所以在此基础上改进,产生了poll…...
Webpack打包流程
轻松了解Webpack 打包流程 Webpack是一个现代的JavaScript应用程序的静态模块打包器。它将多个JavaScript文件打包成一个或多个静态资源文件,以便在浏览器中加载。Webpack将应用程序视为一个依赖项图,其中包括应用程序的所有模块,然后通过该…...
React事件委托
React 事件委托(Event Delegation)是一种优化事件处理的技术,它通过将事件监听器添加到父级元素(而不是子元素)来实现。当事件触发时,事件会向上冒泡到父元素,然后在父元素上调用事件处理函数。…...

Notion——构建个人知识库
前言 使用Notion快三年了,它All in one的理念在使用以后确实深有体会,一直想找一个契机将这个软件分享给大家,这款笔记软件在网上已经有很多的教程了,所以在这里我主要想分享框架方面的内容给大家,特别对于学生党、研究…...

ModuleNotFoundError: No module named ‘Multiscaledeformableattention‘
在实现DINO Detection方法时,我们可能会遇到以上问题。因为在DeformableAttention模块,为了加速,需要自己去编译这个模块。 如果你的环境变量中能够找到cuda路径,使用正确的torch版本和cuda版本的话,这个问题很容易解…...

【数据结构】链表(C语言实现)
创作不易,本篇文章如果帮助到了你,还请点赞 关注支持一下♡>𖥦<)!! 主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步! 🔥c语言系列专栏:c语言之路重点知识整合 &#x…...

【2023程序员必看】大数据行业分析
1、政策重点扶持,市场前景广阔 2014年,大数据首次写入政府工作报告,大数据逐渐成为各级政府关注的热点。 2015年9月,国务院发布《促进大数据发展的行动纲要》,大数据正式上升至国家战略层面,十九大报告提…...

通达信SCTR强势股选股公式,根据六个技术指标打分
SCTR指标(StockCharts Technical Rank)的思路来源于著名技术分析师约翰墨菲,该指标根据长、中、短三个周期的六个关键技术指标对股票进行打分,根据得分对一组股票进行排名,从而可以识别出强势股。 与其他技术指标一样,SCTR的设计…...

SpringBoot+Token+Redis+Lua+自动续签极简分布式锁Token登录方案
前言 用SpringBoot做一个项目,都要写登录注册之类的方案 使用Cookie或Session的话,它是有状态的,不符合现代的技术 使用Security或者Shiro框架实现起来比较复杂,一般项目无需用那么复杂 使用JWT它虽然是无状态的,也可…...

多模态:MiniGPT-4
多模态:MiniGPT-4 IntroductionMethodlimitation参考 Introduction GPT-4具有很好的多模态能力,但是不开源。大模型最近发展的也十分迅速,大模型的涌现能力可以很好的迁移到各类任务,于是作者猜想这种能力可不可以应用到多模态模…...

5年时间里,自动化测试于我带来的意义,希望你也能早点知道
摘要:在我有限的软件测试经历里,曾有一段专职的自动化测试经历。 接触自动化 那时第一次上手自动化测试,团队里用的是Python,接口自动化测试的框架是requestsExcelJenkins,APP自动化测试的框架是Appium。 整个公司当…...

【MyBaits】SpringBoot整合MyBatis之动态SQL
目录 一、背景 二、if标签 三、trim标签 四、where标签 五、set标签 六、foreach标签 一、背景 如果我们要执行的SQL语句中不确定有哪些参数,此时我们如果使用传统的就必须列举所有的可能通过判断分支来解决这种问题,显示这是十分繁琐的。在Spring…...

涅槃重生,BitKeep如何闯出千万用户新起点
在全球,BitKeep钱包现在已经有超过千万用户在使用。 当我得知这个数据的时候,有些惊讶,也有点意料之中。关注BitKeep这几年,真心看得出这家公司的发展之迅速。还记得2018年他们推出第一个版本时,小而美,简洁…...

绝地求生 压枪python版
仅做学习交流,非盈利,侵联删(狗头保命) 一、概述 1.1 效果 总的来说,这种方式是通过图像识别来完成的,不侵入游戏,不读取内存,安全不被检测。 1.2 前置知识 游戏中有各种不同的枪械&#x…...
麒麟操作V10SP1系统systemd目标单元
通过命令列出当前系统中所有可用的 systemd 目标单元。 用于被控制系统启动时运行哪些服务和进程,以及系统在运行过程中的行为。 rootkylin:~# systemctl list-units --typetargetUNIT LOAD ACTIVE SUB DESCRIPTION basic.target…...

python基于LBP+SVM开发构建基于fer2013数据集的人脸表情识别模型是种什么体验,让结果告诉你...
本身LBPSVM是比较经典的技术路线用来做图像识别、目标检测,没有什么特殊的地方 fer2013数据集在我之前的博文中也有详细的实践过,如下: 《fer2013人脸表情数据实践》 系统地基于CNN开发实现 《Python实现将人脸表情数据集fer2013转化为图像…...

antd——实现不分页的表格前端排序功能——基础积累
最近在写后台管理系统时,遇到一个需求,就是给表格中的某些字段添加排序功能。注意该表格是不分页的,因此排序可以只通过前端处理。 如下图所示: 在antd官网上是有关于表格排序的功能的。 对某一列数据进行排序,通过…...

案例11:Java超市管理系统设计与实现开题报告
博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…...
@JsonAlias 和 @JsonProperty的使用
JsonAlias 和 JsonProperty 前言一、JsonAlias二、JsonProperty总结 前言 使用场景:主要运用于参数映射。 如:将admin_id 的值赋予adminId 常用于:接收第三方参数,并对参数进行驼峰化或别名。 一、JsonAlias 是在反序列化的时候…...

Grafana系列-统一展示-8-ElasticSearch日志快速搜索仪表板
系列文章 Grafana 系列文章 概述 我们是基于这篇文章: Grafana 系列文章(十二):如何使用 Loki 创建一个用于搜索日志的 Grafana 仪表板, 创建一个类似的, 但是基于 ElasticSearch 的日志快速搜索仪表板. 最终完整效果如下: 📝…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...