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

【计算机网络】IO多路转接之poll

文章目录

  • 一、poll函数接口
  • 二、socket就绪条件
  • 三、poll的优点
  • 四、poll的缺点
  • 五、poll使用案例--只读取数据的server服务器
    • 1.err.hpp
    • 2.log.hpp
    • 3.sock.hpp
    • 4.pollServer.hpp
    • 5.main.cc

一、poll函数接口

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd结构
struct pollfd {int fd; /* file descriptor */short events; /* requested events */short revents; /* returned events */
};

参数说明

fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合.

nfds表示fds数组的长度.

timeout表示poll函数的超时时间, 单位是毫秒(ms)

时间单位:ms

1.>0:在timeout以内,阻塞,否则非阻塞返回一次

2.=0:非阻塞等待

3.<0:阻塞等待

events和revents的取值:

在这里插入图片描述

用户 -> 内核 :要帮我关心一下fd。输入看 :fd + events

内核告诉用户:你要关心fd上面的events中有哪些事件已经就绪了。输出时看:fd + revents

这样达到了输入和输出分离,poll不需要对参数进行重新设定,以及解决了select等待fd有上限的问题

返回结果

返回值小于0, 表示出错;

返回值等于0, 表示poll函数等待超时;

返回值大于0, 表示poll由于监听的文件描述符就绪而返回.

二、socket就绪条件

读就绪

socket内核中, 接收缓冲区中的字节数, 大于等于低水位标记SO_RCVLOWAT. 此时可以无阻塞的读该文件描述符, 并且返回值大于0;

socket TCP通信中, 对端关闭连接, 此时对该socket读, 则返回0;

监听的socket上有新的连接请求;

socket上有未处理的错误;

写就绪

socket内核中, 发送缓冲区中的可用字节数(发送缓冲区的空闲位置大小), 大于等于低水位标记

SO_SNDLOWAT, 此时可以无阻塞的写, 并且返回值大于0;

socket的写操作被关闭(close或者shutdown). 对一个写操作被关闭的socket进行写操作, 会触发SIGPIPE信号;

socket使用非阻塞connect连接成功或失败之后;

socket上有未读取的错误;

异常就绪

socket上收到带外数据. 关于带外数据, 和TCP紧急模式相关(TCP协议头中, 有一个紧急指针的字段),

三、poll的优点

不同与select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现.

pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式. 接口使用比select更方便.

poll并没有最大数量限制 (但是数量过大后性能也是会下降)

四、poll的缺点

poll中监听的文件描述符数目增多时,和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符.

每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中.

同时连接的大量客户端在一时刻可能只有很少的处于就绪状态, 因此随着监视的描述符数量的增长, 其效率也会线性下降.

五、poll使用案例–只读取数据的server服务器

1.err.hpp

#pragma onceenum
{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,LISTEN_ERR
};

2.log.hpp

#pragma once#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <stdarg.h>#define NORMAL 0
#define DEBUG 1
#define WARNING 2
#define ERROR 3
#define FATAL 4#define LOG_NORMAL "./log.txt"
#define LOG_ERR "./err.txt"#define NUM 1024const char *to_levelstr(int level)
{switch (level){case DEBUG:return "DEBUG";case NORMAL:return "NORMAL";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return nullptr;}
}void LogMessage(int level, const char *format, ...)
{// [日志等级] [时间戳/时间] [pid] [messge]char logprofix[NUM];snprintf(logprofix, sizeof logprofix, "[%s][%ld][pid:%d]", to_levelstr(level), (long int)time(nullptr), getpid());char logcontent[NUM];va_list arg;va_start(arg, format);vsnprintf(logcontent, sizeof logcontent, format, arg);std::cout << logprofix << logcontent << std::endl;FILE *log = fopen(LOG_NORMAL, "a");FILE *error = fopen(LOG_ERR, "a");if (log && error){FILE *cur = nullptr;if (level == DEBUG || level == NORMAL || level == WARNING)cur = log;if (level == ERROR || level == FATAL)cur = error;if (cur)fprintf(cur, "%s%s\n", logprofix, logcontent);fclose(log);fclose(error);}
}

3.sock.hpp

#pragma once#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "log.hpp"
#include "err.hpp"class Sock
{static const int backlog = 32;public:// 1. 创建socket文件套接字对象static int Socket(){int sock = socket(AF_INET, SOCK_STREAM, 0);if (sock < 0){LogMessage(FATAL, "create socket error");exit(SOCKET_ERR);}LogMessage(NORMAL, "create socket success:%d", sock);int opt = 1;setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);return sock;}// 2.bind自己的网络信息static void Bind(int sock, const uint16_t &port){struct sockaddr_in local;memset(&local, 0, sizeof local);local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = INADDR_ANY;int n = bind(sock, (struct sockaddr *)&local, sizeof local);if (n < 0){LogMessage(FATAL, "socket bind error");exit(BIND_ERR);}LogMessage(NORMAL, "socket bind success");}// 3. 设置socket 为监听状态static void Listen(int sock){int n = listen(sock, backlog);if (n < 0){LogMessage(FATAL, "socket listen error");exit(LISTEN_ERR);}LogMessage(NORMAL, "socket listen success");}static int Accept(int listensock, std::string *clientip, uint16_t *clientport){struct sockaddr_in peer;memset(&peer, 0, sizeof peer);socklen_t len = sizeof(peer);int sock = accept(listensock, (struct sockaddr *)&peer, &len);if (sock < 0){LogMessage(ERROR, "socket accept error,next");}else{LogMessage(NORMAL, "accept a new link success, get new sock: %d", sock);*clientip = inet_ntoa(peer.sin_addr);*clientport = ntohs(peer.sin_port);}return sock;}
};

4.pollServer.hpp

#pragma once#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <string>
#include <functional>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <poll.h>#include "sock.hpp"
#include "log.hpp"
#include "err.h"namespace poll_ns
{const static int defaultport = 8080;const static int num = 2048;const static int defaultfd = -1;typedef std::function<std::string(const std::string &)> func_t;class pollServer{public:pollServer(const func_t &func, const uint16_t &port = defaultport): _port(port), _func(func), _listensock(-1), _rfds(nullptr){}void initServer(){_listensock = Sock::Socket();Sock::Bind(_listensock, _port);Sock::Listen(_listensock);_rfds = new struct pollfd[num];for (int i = 0; i < num; i++)ResetItem(i);_rfds[0].fd = _listensock;_rfds[0].events = POLLIN;_rfds[0].revents = 0;}void ResetItem(int pos){_rfds[pos].fd = defaultfd;_rfds[pos].events = 0;_rfds[pos].revents = 0;}void Print(){std::cout << "fd list: ";for (int i = 0; i < num; i++){if (_rfds[i].fd != defaultfd){std::cout << _rfds[i].fd << " ";}}}void Accepter(int listensock){std::string clientip;uint16_t clientport;int sock = Sock::Accept(listensock, &clientip, &clientport);if (sock < 0)return;LogMessage(NORMAL, "accept success[%s:%d]", clientip.c_str(), clientport);int i = 0;for (; i < num; i++){if (_rfds[i].fd == defaultfd)continue;elsebreak;}if (i == num){LogMessage(WARNING, "server is full,please wait");close(sock);}else{_rfds[i].fd = sock;_rfds[i].events = POLLIN;_rfds[i].revents = 0;}Print();}void Recver(int pos){// 1. 读取requestchar buffer[1024];ssize_t s = recv(_rfds[pos].fd, buffer, sizeof(buffer) - 1, 0); // 这里在进行读取的时候,会不会被阻塞?1, 0if (s > 0){buffer[s] = 0;LogMessage(NORMAL, "client# %s", buffer);}else if (s == 0){close(_rfds[pos].fd);ResetItem(pos);LogMessage(NORMAL, "client quit");return;}else{close(_rfds[pos].fd);ResetItem(pos);LogMessage(ERROR, "client error:%s", strerror(errno));return;}// 2. 处理requeststd::string response = _func(buffer);// 3. 返回responsewrite(_rfds[pos].fd, response.c_str(), response.size());}void HandlerEvent(){for (int i = 0; i < num; i++){// 过滤掉非法的fdif (_rfds[i].fd == defaultfd)continue;if (!(_rfds[i].events & POLLIN))continue;if (_rfds[i].fd == _listensock && (_rfds[i].events & POLLIN))Accepter(_listensock);else if (_rfds[i].events & POLLIN)Recver(i);else{}}}void start(){for (;;){int timeout = -1;int n = poll(_rfds, num, timeout);switch (n){case 0:LogMessage(NORMAL, "timeout...");break;case -1:LogMessage(WARNING, "select error,code: %d, err string: %s", errno, strerror(errno));break;default:// 说明有事件就绪了,目前只有一个监听事件就绪了LogMessage(NORMAL, "have event ready!");HandlerEvent();break;}}}~pollServer(){if (_listensock > 0)close(_listensock);if (_rfds)delete[] _rfds;}private:int _port;int _listensock;struct pollfd *_rfds;func_t _func;};
}

5.main.cc

#include "pollServer.hpp"
#include "err.hpp"
#include <memory>using namespace std;
using namespace poll_ns;static void Usage(const string proc)
{std::cerr << "Usage:\n\t" << proc << " port\n\n";
}string transaction(const string &request)
{return "pollServer# " + request;
}int main(int argc, char *argv[])
{// if (argc != 2)// {//     Usage(argv[0]);//     exit(USAGE_ERR);// }std::unique_ptr<pollServer> svr(new pollServer(transaction));svr->initServer();svr->start();return 0;
}

相关文章:

【计算机网络】IO多路转接之poll

文章目录 一、poll函数接口二、socket就绪条件三、poll的优点四、poll的缺点五、poll使用案例--只读取数据的server服务器1.err.hpp2.log.hpp3.sock.hpp4.pollServer.hpp5.main.cc 一、poll函数接口 #include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int t…...

性能比较:in和exists

当在Hive SQL中使用NOT IN和NOT EXISTS时&#xff0c;性能差异主要取决于底层数据的组织方式、数据量大小、索引的使用情况以及具体查询的复杂程度。下面是对这两种方法的性能分析&#xff1a; 1. NOT IN&#xff1a;- 工作原理&#xff1a;NOT IN子查询会逐个比较主查询中的值…...

【Java设计模式】五、建造者模式

文章目录 1、建造者模式2、案例&#xff1a;共享单车的创建3、其他用途 1、建造者模式 某个对象的构建复杂将复杂的对象的创建 和 属性赋值所分离&#xff0c;使得同样的构建过程可以创建不同的表示建造的过程和细节调用者不需要知道&#xff0c;只需要通过构建者去进行操作 …...

nginx代理minio教程 避坑过的教程 避开SignatureDoesNotMatch

本次教程使用的是单机minio进行演示&#xff0c;集群minio也和这个差不多。 按照这个教程&#xff0c;可以避开nginx代理minio之后&#xff0c;只能访问文件&#xff0c;但是通过预签名url上传文件就会报SignatureDoesNotMatch的坑 暂定如下&#xff1a; 你已经下载好miniom…...

Linux进程详细介绍

文章目录 Linux进程1、计算机体系结构和操作系统管理1.1、计算机体系结构 -- 硬件1.2、操作系统&#xff08;Operator System&#xff09; -- 软件 2、进程2.1、进程基本概念2.2、进程标识符2.2.1、获取当前进程标识符和当前进程的父进程标识符2.2.2、通过系统调用创建进程 -- …...

2024年3月产品认证基础考试简答题及答案

产品认证基础 46.产品认证的工厂检查有哪几种路线&#xff1f;各有什么优缺点&#xff1f; 答案&#xff1a;两种常用的检查路线&#xff1a; 1.按照要素或过程检查 按照认证规则规定的工厂应满足的要素要求&#xff08;包括质量保证能力要求&#xff09;&#xff0c;结合部…...

嵌入式蓝桥杯做题总结

第十二届省赛 按键代码 ——自认为比较巧妙&#xff0c;定时器3被设置为10ms进入一次中断&#xff0c;代替了HAL_Delay(10)的方法消抖&#xff1b; 运用状态机机思想实现检测多个按键检测——且分为两个状态&#xff0c;其中一个状态PB&#xff11;和PB&#xff12;的按键不…...

Spring Boot 常用注解大全

以下是Spring Boot中常用的注解及其详细解释以及相应的代码示例&#xff1a; SpringBootApplication: 这个注解用于标识一个Spring Boot应用的主类。它整合了 Configuration&#xff0c;EnableAutoConfiguration 和 ComponentScan。 SpringBootApplication public class Demo…...

(MATLAB)第十二章-数列与极限

目录 12.1 数列 12.1.1 数列求和 1. 累计求和函数sum() 2. 忽略NaN累计求和函数 nansum() 3. 求此元素位置之前的元素和函数cumsum() 4. 求梯形累计和函数cumtrapz() 12.1.2 数列求积 1. 元素连续相乘函数 prod() 2. 求累计积函数 cumprod() 3. 阶乘函数 ffactorial(n…...

OJ输入问题+准备

写在之前&#xff1a; 发现题目输入是这样的&#xff1a; 我的问题&#xff1a;如何通过空格分割这些输入的字符串并分别保存&#xff01;&#xff01;&#xff08;C语言scanf好解决一点但我选择C....&#xff09; C引入了ostringstream、istringstream、stringstream这三个类…...

软考高级:主动攻击和被动攻击概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…...

cuda python torch 虚拟环境配置

以下是Pytorch和CUDA对应的版本 以下是Pytorch和Python对应的版本 检查cuda与Python版本是否匹配 import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.empty(3,4,devicecuda))cuda 删除cuda conda uninstall cudatoolkit --forceconda u…...

激光炸弹 刷题笔记

前置知识 二维前缀和 子矩阵的和 刷题笔记 {二维前缀和}-CSDN博客 思路 参考二维前缀和 将子矩阵的和 做成动态矩阵 一个个矩阵搜索 符合要求边长 矩阵中的元素和最大值 将x1,y1用i-k,j-k表示即可 x2,y2用i&#xff0c;j表示 代码 #include<iostream> #include<…...

Vue3学习记录(三)--- 组合式API之生命周期和模板引用

一、生命周期 1、简介 ​ 生命周期&#xff0c;指的是一个 Vue 实例从创建到销毁的完整阶段&#xff0c;强调的是一个时间段。 ​ 生命周期钩子函数&#xff0c;指的是 Vue 实例提供的内置函数&#xff0c;函数的参数为一个回调函数。这些钩子函数会在实例生命周期的某些固定…...

Batch Normalization和Layer Normalization和Group normalization

文章目录 前言一、Group normalization二、批量规范化(Batch Normalization)三、层规范化&#xff08;Layer Normalization&#xff09; 前言 批量规范化和层规范化在神经网络中的每个批次或每个层上进行规范化&#xff0c;而GroupNorm将特征分成多个组&#xff0c;并在每个组内…...

命名实体识别NER(综合代码示例)

一、命名实体识别发展方向 二、中文数据集 CCKS2017开放的中文的电子病例测评相关的数据。 评测任务一&#xff1a;https://biendata.com/competition/CCKS2017_1/ 评测任务二&#xff1a;https://biendata.com/competition/CCKS2017_2/ CCKS2018开放的音乐领域的实体识别任务…...

关于jQuery日历插件:daterangepicker

关于options singleDatePicker: false 双日历&#xff0c;true 单日历 timePicker&#xff1a;false 不显示时分秒&#xff0c;true 显示时分秒 timePickerIncreament&#xff1a;1 默认值 {singleDatePicker : false, //是否显示单日历框 dateLimit : { days : 30 }, //起…...

【贪心算法】最大子序和

给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4] 输出&#xff1a;6…...

爬虫逆向网站案例

一、相关网页 东方财富人气排行榜 二、查找url 三、寻找curl并复制 四、打开Convert curl commands to code (curlconverter.com) 五、修改并执行代码 import requestscookies {st_si: 73974981954644,st_pvi: 39724919122964,st_sp: 2024-03-05%2018%3A27%3A22,st_inirUrl:…...

蓝桥杯复习之前缀和

题目链接&#xff1a;https://www.luogu.com.cn/problem/P8649 思路&#xff1a; 看到区间和&#xff0c;第一反应肯定是前缀和&#xff0c;我们求出前缀和后对前缀和数组每一个值模k&#xff0c;然后对一个数组的值查看前面有几个相同的&#xff0c;举个例子&#xff1a;…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...