Linux学习记录——사십이 高级IO(3)--- Poll型服务器

文章目录

  • 1、认识poll接口
  • 2、实现
  • 3、特点


1、认识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 */
};

poll返回值和select一样,都是int,表示就绪的fd数量。timeout是一个输入型参数,单位是毫秒ms,为0表示非阻塞,小于0表示阻塞,大于0poll在这段时间内阻塞等待,如果一直没有事件就绪,那么超过时间就返回0。fds相当于一个数组,events是用户关心的fd上的事件,revents是内核告诉用户,关心的fd中哪些已经有事件就绪。

poll分离了输入参数和输出参数,这样就不需要在调用poll时进行重新设置了。

在这里插入图片描述

上图的事件其实都是宏,POLLIN表示读事件就绪,POLLOUT表示写事件就绪。

2、实现

基于上一篇select的代码来实现,代码只用到能够等待多个fd那里。基本代码

#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <sys/poll.h>
#include "Sock.hpp"
#include "log.hpp"
#include "err.hpp"const static int gport = 8888;typedef int type_t;class PollServer
{static const int N = (sizeof(fd_set) * 8);public:PollServer(uint16_t port = gport) : port_(port) {}void InitServer(){listensock_.Socket();listensock_.Bind(port_);listensock_.Listen();for (int i = 0; i < N; i++)fdarray_[i] = defaultfd;}void Accepter(){std::string clientip;uint16_t clientport;int sock = listensock_.Accept(&clientip, &clientport);if (sock < 0)return;logMessage(Debug, "[%s:%d], sock: %d", clientip.c_str(), clientport, sock);int pos = 1;for (; pos < N; pos++){if (fdarray_[pos] == defaultfd)break;}if (pos >= N){close(sock);logMessage(Warning, "sockfd array[] full");}else{fdarray_[pos] = sock;}}void HandlerEvent(fd_set &rfds){for (int i = 0; i < N; i++){if (fdarray_[i] == defaultfd)continue;if ((fdarray_[i] == listensock_.Fd()) && FD_ISSET(listensock_.Fd(), &rfds)){Accepter();}else if ((fdarray_[i] != listensock_.Fd()) && FD_ISSET(fdarray_[i], &rfds)){int fd = fdarray_[i];char buffer[1024];ssize_t s = recv(fd, buffer, sizeof(buffer) - 1, 0);if (s > 0){buffer[s-1] = 0;std::cout << "client# " << buffer << std::endl;std::string echo = buffer;echo += " [select server echo]";send(fd, echo.c_str(), echo.size(), 0);}else{if (s == 0)logMessage(Info, "client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultfd);elselogMessage(Warning, "recv error, client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultfd);close(fdarray_[i]);fdarray_[i] = defaultfd;}}}}void Start(){fdarray_[0] = listensock_.Fd();while (true){fd_set rfds;FD_ZERO(&rfds);int maxfd = fdarray_[0];for (int i = 0; i < N; i++){if (fdarray_[i] == defaultfd)continue;// 合法fdFD_SET(fdarray_[i], &rfds);if (maxfd < fdarray_[i])maxfd = fdarray_[i];}int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);switch (n){case 0:logMessage(Debug, "timeout, %d: %s", errno, strerror(errno));break;case -1:logMessage(Warning, "%d: %s", errno, strerror(errno));break;default:logMessage(Debug, "有一个就绪事件发生了: %d", n);HandlerEvent(rfds);DebugPrint();break;}}}void DebugPrint(){std::cout << "fdarray[]: ";for (int i = 0; i < N; i++){if (fdarray_[i] == defaultfd)continue;std::cout << fdarray_[i] << " ";}std::cout << "\n";}~PollServer(){listensock_.Close();}private:uint16_t port_;Sock listensock_;type_t fdarray_[N];
};

改一下数组类型

typedef struct pollfd type_t;
type_t* fdarray_;

在构造函数那里初始化为nullptr,然后在初始化函数初始化

const static int gport = 8888;
const static int N = 4096;
const static short defaultevent = 0;typedef struct pollfd type_t;class PollServer
{
public:PollServer(uint16_t port = gport) : port_(port), fdarray_(nullptr){}void InitServer(){listensock_.Socket();listensock_.Bind(port_);listensock_.Listen();fdarray_ = new type_t[N];for (int i = 0; i < N; i++){fdarray_[i].fd = defaultfd;fdarray_[i].events = defaultevent;fdarray_[i].revents = defaultevent;}}~PollServer(){listensock_.Close();if(fdarray_) delete []fdarray_;//判断不为空再delete}

start函数里这些不再需要

            fd_set rfds;FD_ZERO(&rfds);int maxfd = fdarray_[0].fd;for (int i = 0; i < N; i++){if (fdarray_[i] == defaultfd)continue;// 合法fdFD_SET(fdarray_[i], &rfds);if (maxfd < fdarray_[i])maxfd = fdarray_[i];}
    void Start(){fdarray_[0].fd = listensock_.Fd();fdarray_[0].events = POLLIN;while (true){int timeout = 1000;int n = poll(fdarray_, N, timeout);switch (n){case 0:logMessage(Debug, "timeout, %d: %s", errno, strerror(errno));break;case -1:logMessage(Warning, "%d: %s", errno, strerror(errno));break;default:logMessage(Debug, "有一个就绪事件发生了: %d", n);HandlerEvent(rfds);DebugPrint();break;}}}

poll函数那里,第二个参数可以不传N,把要管理的fd都放到fdarray_最左侧排起来,第二个参数就可以只看这几个fd的个数。

其它函数更改一下

    void Accepter(){std::string clientip;uint16_t clientport;int sock = listensock_.Accept(&clientip, &clientport);if (sock < 0)return;logMessage(Debug, "[%s:%d], sock: %d", clientip.c_str(), clientport, sock);int pos = 1;for (; pos < N; pos++){if (fdarray_[pos].fd == defaultfd)break;}if (pos >= N){//可以先动态扩容,扩容失败再closeclose(sock);logMessage(Warning, "sockfd array[] full");}else{fdarray_[pos].fd = sock;fdarray_[pos].events = POLLIN;//可以设置成POLLIN / POLLOUT,也就是读写事件都关心fdarray_[pos].revents = defaultevent;}}void HandlerEvent(){for (int i = 0; i < N; i++){int fd = fdarray_[i].fd;short revent = fdarray_[i].events;if (fd == defaultfd)continue;if ((fd == listensock_.Fd()) && (revent & POLLIN))//fd是需要的fd且读数据就绪{Accepter();}else if ((fd != listensock_.Fd()) && (revent & POLLIN))//fd不是我们要的,但读数据就绪{int fd = fdarray_[i].fd;char buffer[1024];ssize_t s = recv(fd, buffer, sizeof(buffer) - 1, 0);if (s > 0){buffer[s-1] = 0;std::cout << "client# " << buffer << std::endl;std::string echo = buffer;echo += " [select server echo]";send(fd, echo.c_str(), echo.size(), 0);}else{if (s == 0)logMessage(Info, "client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultfd);elselogMessage(Warning, "recv error, client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultfd);close(fd);fdarray_[i].fd = defaultfd;fdarray_[i].events = defaultevent;fdarray_[i].revents = defaultevent;}}}}

makefile

pollserver:main.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f pollserver

main.cc

#include "PollServer.hpp"
#include <memory>int main()
{//fd_set fd;//std::cout << sizeof(fd) << std::endl;std::unique_ptr<PollServer> svr(new PollServer());svr->InitServer();svr->Start();return 0;
}

timeout改成-1就是阻塞了。现在这个服务器支持读,如果要支持写,在HandlerEvent函数里

              if (s > 0){buffer[s-1] = 0;std::cout << "client# " << buffer << std::endl;fdarray_[i].events |= POLLOUT;//也关心写事件std::string echo = buffer;echo += " [select server echo]";send(fd, echo.c_str(), echo.size(), 0);}

3、特点

poll虽然相对于select简单了好多,不过poll也是以数组形式传多个fd,让操作系统去遍历所有fd,所以对于底层的消耗和select一样。但poll解决了fd上限少的问题,数组多大是用户决定的,而select自己就决定了一个fd_set,让用户只得用fd_set。当用户定的数组太大时,操作系统去遍历,效率也低。所以poll对于select,虽然看上去简单了一些,但放到实际中,文件逐渐增多,两者都不怎么样,不过poll写起来更简单。

select可以跨平台,poll不行。


本篇gitee

结束。

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

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

相关文章

BitMap源码解析

文章目录 前言数据结构添加与删除操作 JDK中BitSet源码解析重要成员属性初始化添加数据清除数据获取数据size和length方法集合操作&#xff1a;与、或、异或优缺点 前言 为什么称为bitmap&#xff1f; bitmap不仅仅存储介质以及数据结构不同于hashmap&#xff0c;存储的key和v…

5.3 Verilog 带参数例化

5.3 Verilog 带参数例化 分类 Verilog 教程 关键词&#xff1a; defparam&#xff0c;参数&#xff0c;例化&#xff0c;ram 当一个模块被另一个模块引用例化时&#xff0c;高层模块可以对低层模块的参数值进行改写。这样就允许在编译时将不同的参数传递给多个相同名字的模块…

element:日历 / 使用记录

一、预期效果 Element - The worlds most popular Vue UI framework element默认样式 目标样式 二、Calendar 属性 参数说明类型可选值默认值value / v-model绑定值Date/string/number——range时间范围&#xff0c;包括开始时间与结束时间。开始时间必须是周一&#xff0c;…

c语言线性方式初始化二维数组

线性方式初始化二维数组&#xff0c;只需要利用/与%的关系即可。具体细节文章下面会有程序的流程分析 问题起源 想要用线性方式初始化二维数组 问题分析 例如a[3][4] a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3]如…

Flutter-Web从0到部署上线(实践+埋坑)

本文字数&#xff1a;7743字 预计阅读时间&#xff1a;60分钟 01 前言 首先说明一下&#xff0c;这篇文章是给具备Flutter开发经验的客户端同学看的。Flutter 的诞生虽然来自 Google 的 Chrome 团队&#xff0c;但大家都知道 Flutter 最先支持的平台是 Android 和 iOS&#xff…

PHP在线文档管理系统源码

PHP在线文档管理系统源码 系统功能与介绍 在数据持续、快速增长背景下&#xff0c;企业面临海量非结构化数据处理需求&#xff0c;企业现有架构 通常无法应对海量非结构化数据的管理与应用。 支持私有化部署&#xff0c;完全内网环境下也可正常使用。 Windows、Linux、Mac等全平…

7个向量数据库对比:Milvus、Pinecone、Vespa、Weaviate、Vald、GSI 和 Qdrant

本文简要总结了当今市场上正在积极开发的7个向量数据库&#xff0c;Milvus、Pinecone、Vespa、Weaviate、Vald、GSI 和 Qdrant 的详细比较。 我们已经接近在搜索引擎体验的基础层面上涉及机器学习&#xff1a;在多维多模态空间中编码对象。这与传统的关键字查找不同&#xff08…

通过代理连接sftp

通过nginx代理连接sftp 1.问题描述2.代码实现3.nginx配置3.1 创建sftp.stream文件3.2 修改nginx配置 4.重启nginx生效 1.问题描述 问题是这样的。我们现在需要在微服务所在内网的A机器连接到外网的sftp&#xff0c;但是网络又不能直接到达。然后A机器到B机器是通过的&#xff…

【SAP】如何删除控制范围

经历就是财富&#xff0c;可你终将遗忘。期望文字打败时间。 本周心惊胆战地在配置系统删除了一个控制范围&#xff0c;还是有些收获&#xff0c;特此记录一下。 背景&#xff1a;在删除控制范围之前&#xff0c;我主要做了如下配置。 定义控制范围&#xff08;自动生成了成本…

【UEFI基础】EDK网络框架(IP4)

IP4 IP4协议说明 IP全称Internet Protocol&#xff0c;它属于网络层&#xff0c;对其下各种类型的数据链路层进行了包装&#xff0c;这样网络层可以跨越不同的数据链路&#xff0c;即使是在不同的数据链路上也能实现两端节点之间的数据包传输。 IP层的主要作用就是“实现终端…

C++|19.C++类与结构体对比

类和结构体 类和结构体本质上并没有太大区别。 但两者在默认上有所区别。 类默认成员变量是私有的&#xff0c;而结构体默认成员变量是公有的。 也就是说&#xff0c;对于一个类来说&#xff0c;会默认使用private去保护其内部成员变量使得无法直接访问到其内部的变量。 同时从…

代码随想录算法训练营第27天 | 39. 组合总和 40.组合总和II 131.分割回文串

目录 39. 组合总和 &#x1f4a1;解题思路 &#x1f4bb;实现代码 40.组合总和II &#x1f4a1;解题思路 &#x1f4bb;实现代码 131.分割回文串 &#x1f4a1;解题思路 # 判断回文子串 &#x1f4bb;实现代码 39. 组合总和 题目链接&#xff1a;39. 组合总和 给定…

C++ 开发 + VSCode 调试

C 开发 VSCode 调试 MSYS2 安装 gcc、make下载安装MSMYS2pacman 添加镜像源 GCC1. 安装2. 查看结果3. 环境变量 GDB VSCode 调试所需插件创建项目调试代码1. tasks.json 配置任务2. launch.json 配置调试3. 运行 更进一步的 C/C 设置 参考资料 MSYS2 安装 gcc、make 下载 官…

UCB Data100:数据科学的原理和技巧:第二十一章到第二十六章

二十一、SQL II 原文&#xff1a;SQL II 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 学习成果 介绍过滤组的能力 在 SQL 中执行数据清理和文本操作 跨表连接数据 在本讲座中&#xff0c;我们将继续上次的工作&#xff0c;介绍一些高级的 SQL 语法。 首先&…

解锁营销新高度:幽灵鲨CRM推广平台线索对接功能详解

数字营销时代&#xff0c;线索对接是推动业务增长的关键。你是否为线索分布在不同的平台而来回切换&#xff1f;你是否为无法及时联系客户而错失商机&#xff1f;幽灵鲨CRM系统作为一款领先的客户关系管理解决方案&#xff0c;不仅实现了对主流推广平台的全面对接&#xff0c;更…

C++内存分配策略

目录 基础概念 内存布局 分配方式 实现 1.new和delete 2.利用空间配置器alloc 3.用malloc和free 4.静态内存分配 基础概念 在讲内存分配之前&#xff0c;先对一些基础概念进行阐述&#xff0c;以便能更好的讨论问题 内存布局 代码编译为可执行程序后运行占用的内存可…

了解统计分类中的贝叶斯理论误差限

一、介绍 统计分类和机器学习领域正在不断发展&#xff0c;努力提高预测模型的准确性和效率。这些进步的核心在于一个基本基准&#xff0c;即贝叶斯理论误差极限。这个概念深深植根于概率和统计学&#xff0c;是理解分类算法的局限性和潜力的基石。本文深入探讨了贝叶斯错误率的…

【LabVIEW FPGA入门】使用LabVIEW FPGA进行编程并进行编译

在本文中会进行一个简单的FPGA编程演示&#xff0c;这通常可以验证编译工具链是否正常使用。在LabVIEW FPGA中和rt、PC编程一样使用数据流编程&#xff0c;但是需要注意的是FPGA中有些函数是不可以用的&#xff0c;因为这些函数很占用资源&#xff0c;且FPGA只能同时下载运行一…

AI软件开发:探索原理、挑战与未来趋势

AI软件开发已经成为当前最热门和具有前景的技术领域之一。随着人工智能技术的快速发展&#xff0c;AI软件的应用范围也在不断扩大。本文将主要探讨AI软件开发的原理、挑战以及未来的趋势。 首先&#xff0c;AI软件开发的原理是基于机器学习和深度学习算法。机器学习是一种通过…

Jetbrains ai assistant激活成功了

使用ai assistant插件助手 很完美&#xff0c;第一次用在idea 开发工具就完美的把激活了&#xff0c;你也不妨试试 链接地址&#xff1a;https://web.52shizhan.cn 激活后如下 登录页面 完美使用