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 的日志快速搜索仪表板. 最终完整效果如下: 📝…...

【K8s】openEuler23操作系统安装Docker和Kubernetes
openEuler23操作系统安装 服务器搭建环境随手记 文章目录 openEuler23操作系统安装前言:一、前期准备(所有节点)1.1所有节点,关闭防火墙规则,关闭selinux,关闭swap交换,打通所有服务器网络&am…...

异常数据检测 | Python实现ADTK时间序列异常数据检测
文章目录 文章概述模型描述程序设计参考资料文章概述 异常数据检测 | Python实现ADTK时间序列异常数据检测 智能运维AIOps的数据基本上都是时间序列形式的,而异常检测告警是AIOps中重要组成部分。 模型描述 笔者最近在处理时间序列数据时有使用到adtk这个python库,在这里和大…...

软件测试之jmeter性能测试让你打开一个全新的世界
一、Jmeter简介 1 概述 jmeter是一个软件,使负载测试或业绩为导向的业务(功能)测试不同的协议或技术。 它是 Apache 软件基金会的Stefano Mazzocchi JMeter 最初开发的。 它主要对 Apache JServ(现在称为如 Apache Tomcat…...

Redis数据结构——动态字符串、Dict、ZipList
一、Redis数据结构-动态字符串 我们都知道Redis中保存的Key是字符串,value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。 不过Redis没有直接使用C语言中的字符串,因为C语言字符串存在很多问题: 获取字符串长度…...

ipad可以用别的品牌的手写笔吗?便宜的ipad电容笔
而对于那些把ipad当做学习工具的人而言,苹果Pencil就成了必备品。但因为苹果Pencil太贵了,学生们买不起。因此,最好的选择还是平替电容笔。作为一个ipad的忠实用户,同时也是一个数字热爱着,这两年来,我一直…...

【数据库】关于SQL SERVER的排序规则的问题分析
在安装报表系统,运行sql语句时候提示“无法解决 equal to 操作的排序规则冲突。”,费了半天时间才搞定,原来是因为sql语句中没有加全collate Chinese_PRC_CI_AI_WS ! 用排序规则特点计算汉字笔划和取得拼音首字母 SQL SERVER的…...

算法修炼之练气篇——练气十三层
博主:命运之光 专栏:算法修炼之练气篇 目录 题目 1023: [编程入门]选择排序 题目描述 输入格式 输出格式 样例输入 样例输出 题目 1065: 二级C语言-最小绝对值 题目描述 输入格式 输出格式 样例输入 样例输出 题目 1021: [编程入门]迭代法求…...

ChatGPT:AI不取代程序员,只取代的不掌握AI的程序员
作者:成都兰亭集势信息技术有限公司技术总监张雄 可能大家会有如下的问题,我就使用chatGPT这个AI工具的API来问一下。 问:chatGPT会替换掉程序员吗?如果能,预计好久? 答:作为一名 AI 语言模型&a…...

数字革命下的产品:百数十年变迁的启示与思考。
随着数字化时代的到来,软件开发成为各行各业不可或缺的一部分。然而,传统的软件开发方法需要长时间的开发周期,高昂的成本和大量的人力资源。因此,低代码开发平台应运而生。低代码开发平台通过简化开发人员的工作和加速软件开发流…...

部门新来一00后,给我卷崩溃了...
2022年已经结束结束了,最近内卷严重,各种跳槽裁员,相信很多小伙伴也在准备今年的金三银四的面试计划。 在此展示一套学习笔记 / 面试手册,年后跳槽的朋友可以好好刷一刷,还是挺有必要的,它几乎涵盖了所有的…...