【计算机网络】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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/719818.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“羊驼“入侵CV,美团浙大沈春华团队将LLaMA向CV扩展,构建全新基础模型VisionLLaMA

本文首发:AIWalker https://arxiv.org/abs/2403.00522 https://github.com/Meituan-AutoML/VisionLLaMA 本文概述 大型语言模型构建在基于Transformer的架构之上来处理文本输入, LLaMA 系列模型在众多开源实现中脱颖而出。类似LLaMa的Transformer可以用来处理2D图像吗&#xf…

Python绘制不同形状词云图

目录 1.基本词云图1.1 导入所需库1.2 准备词汇1.3 配置参数并生成词云图1.4 在Python窗口中显示图片1.5 效果展示1.6 完整代码 2. 不同形状词云图2.1 找到自己所需形状图片2.2 利用PS将图片设置为黑白色2.3 在代码中设置背景2.4 效果展示 1.基本词云图 1.1 导入所需库 import…

远程调用--webClient

远程调用webClient 前言1、创建webClient2、准备数据3、执行请求4、接收返回响应到的数据整体代码 前言 非阻塞、响应式HTTP客户端 1、创建webClient WebClient client WebClient.create();2、准备数据 Map<String,String> params new HashMap<>();params.pu…

贪心算法(区间问题)

452. 用最少数量的箭引爆气球 题目(求无重复区间) 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 一支弓箭可以沿着…

利用Python爬取8684公交路线查询网站中全国公交站点信息

利用python语言结合requests、BeautifulSoup等类库爬取https://api.8684.cn/v3/api.php?docitys&actprovince对应接口中所有城市公交路线信息以及公交站点信息。 import time import requests import json, re from bs4 import BeautifulSoup# 定义一个函数&#xff0c;传…

“祖传代码“的是是非非

程序员眼中的“祖传代码”&#xff0c;就像一本古老而神秘的魔法书&#xff0c;藏着无穷的智慧和技巧&#xff0c;有些代码像家传宝贝&#xff0c;有些像祖传秘方。快来分享一下你遇到的“祖传代码”吧~ 祖传代码的历史与文化价值 祖传代码通常指的是经过长时间使用和传承的代…

【DUSt3R】2张图2秒钟3D重建

【DUSt3R】2张图2秒钟3D重建 1. DUSt3R是一种用于稠密和无约束立体三维重建的方法,其实现步骤如下:2. 实际运行效果3. 运行结果4. 自问自答4.1 为社么这里要是使用transform模型呢?4.2 CroCo(通过跨视图完成3D视觉任务的自我监督预训练的一个研究)在DUSt3R的作用是什么,为…

打家劫舍(java版)

&#x1f4d1;前言 本文主要是【动态规划】——打家劫舍(java版)的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一…

17 easy 290. 单词规律

//给定一种规律 pattern 和一个字符串 s &#xff0c;判断 s 是否遵循相同的规律。 // // 这里的 遵循 指完全匹配&#xff0c;例如&#xff0c; pattern 里的每个字母和字符串 s 中的每个非空单词之间存在着双向连接的对应规律。 // // // // 示例1: // // //输入: patte…

一款不错的多端SSH工具:Xterminal

1、不仅是强大的SSH工具&#xff0c;更提供本地控制台&#xff0c;以及更多即将推出的开发相关功能&#xff0c;让您专注于创造卓越的代码 2、AI赋能&#xff0c;智能命令提示&#xff0c;为大脑解压 AI解答&#xff0c;让你的疑问得到即时解答 AI智能提示&#xff0c;让每一…

CodeFlying 和 aixcoder两大免费软开平台,孰强孰弱?

今天为大家带来码上飞CodeFlying和aixcoder两款免费的软件开发平台效果的测评 一、产品介绍 首先简单介绍一下这两个平台 码上飞CodeFlying&#xff1a;码上飞 CodeFlying | AI 智能软件开发平台&#xff01; 是一款革命性的软件开发平台&#xff0c;它通过将软件工程和大模…

Redis是AP的还是CP的?

redis是一个开源的内存数据库&#xff0c;那么他到底是AP的还是CP的呢&#xff1f; 有人说&#xff1a;单机的是redis是cp的&#xff0c;而集群的redis是ap的&#xff1f; 但是我不这么认为&#xff0c;我觉得redis就是ap的&#xff0c;虽然在单机redis中&#xff0c;因为只有…

Git 基本操作 ⼯作区、暂存区、版本库

创建本地仓库&#xff1a; 创建 Git 本地仓库 要提前说的是&#xff0c;仓库是进行版本控制的⼀个文件目录。我们要想对文件进行版本控制&#xff0c;就必须先创建⼀个仓库出来。 首先touch 一个文件&#xff1a; 初始化仓库&#xff1a; 创建完成后&#xff0c;我们会发现当前…

行列式错题本

《1800》 1 阶数和转置 A是三阶,B是4阶,还有2这个系数 2 怎么啥也不会呀,委屈 行列式的拆分+提取系数 3

uniapp 安装安卓、IOS模拟器并调试

一、安装Android模拟器并调试 1.下载并安装Android Studio。 2.创建简单project。 3.安装模拟器。 完成安卓模拟器的安装。 4.启动模拟器。 5.hbuilderx选择模拟器、运行。 点击刷新按钮后出现模拟器&#xff0c;勾选并运行。 6.调试。 在 HBuilderX 中&#xff0c;项目启…

每天一道leetcode:20.有效的括号(简单;栈的经典题目)

⭐今日份题目 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对…

Nano 33 BLE Sense Rev2学习第一节——环境配置

参考文档见Access Barometric Pressure Sensor Data on Nano 33 BLE Sense | Arduino Documentation 打开Arduino ide安装开发板 选择开发板 连接开发板到电脑&#xff0c;自动识别开发板端口&#xff0c;选择端口

【Java】查看class文件的jdk编译版本的两种方式

一、使用文本编辑工具EditPlus 使用EditPlus打开该class文件&#xff0c;字符集选择16进制&#xff08;Hex viewer&#xff09;。 仅看第一行数据&#xff0c;前面8个字节CA FE BA BE是固定的。 之后4个字节00 00 是次版本。 次版本后面的4个字节00 34 就是jdk版本。 jdk版本…

镜头畸变模型及去畸变的原理

1. OpenCV去畸变undistortPoints原理解析 Opencv中镜头畸变包含了径向畸变和切向畸变&#xff0c;本章节主要阐述镜头畸变模型以及去畸变的原理。 1.1 镜头畸变模型 参考opencv文档 https://docs.opencv.org/3.1.0/d4/d94/tutorial_camera_calibration.html&#xff0c;opencv…

基于SpringBoot+MYSQL的医护人员排班系统

基于springboot的医护人员排班系统录像 1、 前言介绍 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了医护人员排班系统的开发全过程。通过分析医护人员排班系统管理的不足&#xff0c;创建了一个计算机管理医护人员…