多路转接之poll(接口介绍,struct pollfd介绍,实现原理,实现非阻塞网络通信代码)

news2024/12/26 14:16:29

目录

poll

引入

介绍

函数原型

fds

struct pollfd

特点

nfds

timeout

取值

返回值 

原理

如何实现关注多个fd?

如何确定哪个fd上有事件就绪?

如何区分事件类型?

判断某事件是否就绪的方法

代码

示例

总结

为什么说它解决了fd上限问题?

缺点


poll

引入

我们前面介绍了select -- 多路转接之select(fd_set介绍,参数详细介绍,优缺点),实现非阻塞式网络通信(代码+思路)-CSDN博客

  • 随着文件数量增多(有新客户端来连接),文件上事件就绪的概率也就增大了
  • 而等待就绪->通知用户层有事件就绪的过程涉及到多次遍历和拷贝,就绪次数多了,遍历和拷贝就多了
  • 所以,会慢慢让效率变低

为了解决这些问题,出现了新的多路转接的方案 -- poll

 

介绍

是一种用于多路复用 I/O 事件的系统调用,它允许程序监视多个文件描述符,并等待其中的某些事件发生

  • 它和select一样,只负责等待
  • 它主要解决了select的两个弊端 -- fd有上限 和 参数重置问题

 

函数原型

fds

和select中三个位图的作用相同 -- 用于用户和内核之间的信息交流,但原理不同

struct pollfd

  • 用户给内核传入要关注文件的fd和要关注的事件类型 -- 使用了pollfd中的fd,events字段
  • 内核给用户返回该文件上的哪个事件已就绪 -- 使用fd,revents字段
特点

将输入和输出事件分离

  • 而不是像select一样,用户和内核使用的是同一个结构(位图)
  • 这里使用了两个变量给两方分别使用

nfds

fds中的元素个数

  • 相当于需要关注的fd个数

timeout

等待事件的超时时间

  • 毫秒为单位,1000ms=1s
取值
  • >0 -- 超时时间
  • =0 -- 非阻塞,函数会立即返回
  • -1

返回值 

和select作用相同

  • >0 -- 就绪的fd个数
  • =0 -- 超时,没有事件就绪
  • <0 -- 等待的文件中有已经关闭的文件

原理

如何实现关注多个fd?

这里的fds参数是一个结构体类型的指针

  • 所以可以传入结构体数组 / 指向堆上空间的指针,后续可以动态扩容

所以,我们可以添加多个pollfd结构到数组中

如何确定哪个fd上有事件就绪?

遍历数组,检查每个结构的revents字段状态

如何区分事件类型?

将events/revents字段看作16个bit位,1个bit位可以对应一个事件类型

  • 和标志位一样
判断某事件是否就绪的方法
  • 读事件为例:
  • (某个指定pollfd结构中的revents & POLLIN) 是否等于1,等于1说明该事件已经就绪

代码

我们可以直接改select代码为poll版本,很简单:

  • 不需要在循环内重复设置参数
  • 把那些删掉后,修改调用select为poll即可,最多就创建一个pollfd结构体
#include "Log.hpp"
#include "socket.hpp"
#include <poll.h>

static const int def_port = 8080;
static const int def_max_num = 1024;
static const int def_data = -1;
static const int no_data = 0;

class poll_server
{
public:
    poll_server()
    {
        for (int i = 0; i < def_max_num; ++i)
        {
            fds_[i].fd = def_data;
            fds_[i].events = no_data;
            fds_[i].revents = no_data;
        }
    }
    ~poll_server()
    {
        listen_socket_.Close();
    }
    void start()
    {
        listen_socket_.Socket();
        listen_socket_.Bind(def_port);
        listen_socket_.Listen();
        int timeout = 1;

        // 固定数组第一项是监听套接字
        struct pollfd tl = {listen_socket_.get_fd(), POLLIN, no_data};
        fds_[0] = tl;

        while (true)
        {
            int ret = poll(fds_, def_max_num, timeout);
            if (ret > 0) // 有事件就绪
            {
                handle();
            }
            else if (ret == 0) // 超时
            {
                continue;
            }
            else
            {
                perror("poll");
                break;
            }
        }
    }

private:
    void receiver(int fd, int i)
    {
        char in_buff[1024];
        int n = read(fd, in_buff, sizeof(in_buff) - 1);
        if (n > 0)
        {
            in_buff[n - 1] = 0;
            std::cout << "get message: " << in_buff << std::endl;
        }
        else if (n == 0) // 客户端关闭连接
        {
            close(fd);
            lg(DEBUG, "%d quit", fd);
            fds_[i].fd = -1; // 重置该位置
            fds_[i].events = no_data;
            fds_[i].revents = no_data;
        }
        else
        {
            lg(ERROR, "fd: %d ,read error");
        }
    }
    void accepter()
    {
        std::string clientip;
        uint16_t clientport;
        int sock = listen_socket_.Accept(clientip, clientport);
        if (sock == -1)
        {
            return;
        }
        else // 把新fd加入数组
        {
            struct pollfd t;
            int pos = 1000; //1s
            for (; pos < def_max_num; ++pos)
            {
                if (fds_[pos].fd == def_data) // 找到空位,但不能直接添加
                {
                    break;
                }
            }
            if (pos != def_max_num)
            {
                t.fd = sock;
            }
            else // 满了
            {
                //这里可以扩容
                lg(WARNING, "server is full,close %d now", sock);
                close(sock);
            }
            t.events = POLLIN;
            t.revents = no_data;
            fds_[pos] = t;
        }
    }
    void handle()
    {
        for (int i = 0; i < def_max_num; ++i) // 遍历数组
        {
            int fd = fds_[i].fd;
            if (fd != def_data) // 有效fd
            {
                if (fds_[i].revents & POLLIN) // 有事件就绪
                {
                    if (fd == listen_socket_.get_fd()) // 获取新连接
                    {
                        accepter();
                    }
                    else // 读事件
                    {
                        receiver(fd, i);
                    }
                }
            }
        }
    }

private:
    MY_SOCKET listen_socket_;
    struct pollfd fds_[def_max_num];
};

示例

总结

为什么说它解决了fd上限问题?

因为poll里的数组大小由用户决定,而fd_set的大小已经被系统定死了,无法改变

缺点

但是,poll依然没有解决多次遍历的问题

  • 用户层需要查看数组中每个结构的revents,看哪些文件的哪些事件就绪了
  • 内核也需要遍历数组,查看需要关注哪些文件的哪些事件,查看是否有文件就绪

遍历成本由文件个数决定

  • 虽然poll没有限制,但一旦数量过多,会影响遍历效率

所以,为了解决这个问题,提出了新的方案 -- epoll

它是目前效率最高的多路转接方案

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

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

相关文章

DVWA通关教程

Brute Force Low 先进行一下代码审计 <?php // 检查是否通过GET请求传递了Login参数&#xff08;注意&#xff1a;这里应该是username或类似的&#xff0c;但代码逻辑有误&#xff09; if( isset( $_GET[ Login ] ) ) { // 从GET请求中获取用户名 $user $_GET[ us…

【学习笔记】手写 Tomcat -- 预备知识

目录 一、新建项目 二、IO流 1. 什么是IO流&#xff1f; 2. IO的流向说明图解 3. IO 流的分类 4. 字节流 输出流 字节输出流的细节 输入流 字节输入流的细节 5. 练习 6. 字符流 输入流 字符流读取的细节 字符输入流原理解析 字符输出流原理解析 三、网络编程 …

NVIDIA GH200 超级芯片:重塑超算性能与AI基准的革新之作

Nvidia 正在将其 GH200 芯片应用于欧洲超级计算机&#xff0c;研究人员正在着手研究这些系统并发布带有性能基准的研究论文。 在第一篇论文《理解紧密耦合异构系统中的数据移动&#xff1a;以 Grace Hopper 超级芯片为例》中&#xff0c;研究人员对 GH200 的各种应用进行了基准…

vue2关闭eslint

vue2关闭eslint 1、找到项目build目录下的webpack.base.conf.js文件 2、注释createLintingRule()里面的内容&#xff08;只注释里面的内容&#xff09; 3、重启项目即可

自己动手实现mybatis的底层框架(不用动态代理直接用执行器、用动态代理自己实现。图文分析!)

目录 一.原生mybits框架图分析 自己实现Mybatis框架的分析 两种框架操作数据库的方法&#xff1a; 二.搭建开发环境 1.先创建一个maven项目 2.加入依赖(mysql dom4j junit lombok) 三.mybatis框架的设计思路 具体实现过程 3.1实现任务阶段 1- 完成读取配置文件&#x…

基于 TiDB 资源管控 + TiCDC 实现多业务融合容灾测试

导读 随着金融行业的不断发展&#xff0c;多个业务系统的整合成为了趋势&#xff0c;分布式数据库的应用也愈发广泛。为了应对多业务融合带来的复杂性&#xff0c;金融机构需要在保障各业务系统高效运行的同时&#xff0c;确保 IT 系统的高可用性和稳定性。本文将介绍 TiDB 如…

多输入多输出 | Matlab实现DBO-BP蜣螂算法优化BP神经网络多输入多输出预测

多输入多输出 | Matlab实现DBO-BP蜣螂算法优化BP神经网络多输入多输出预测 目录 多输入多输出 | Matlab实现DBO-BP蜣螂算法优化BP神经网络多输入多输出预测预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 多输入多输出 | Matlab实现DBO-BP蜣螂算法优化BP神经网络…

如何选择合适的数据报表工具?

在企业的日常运营中&#xff0c;数据报表如同企业的“仪表盘”&#xff0c;为管理者提供了关键的业务信息。无论是销售数据、财务状况还是生产进度&#xff0c;都需要通过数据报表进行清晰的呈现。同时&#xff0c;随着企业对数据可视化的需求不断增加&#xff0c;数据看板和数…

Numba最近邻插值(CPU+ GPU + Z轴切块 + XYZ轴切块 + 多线程)

文章目录 最近邻插值&#xff08;加速方法&#xff09;&#xff08;1&#xff09;scipy.ndimage.zoom&#xff08;2&#xff09;Numba-CPU加速&#xff08;3&#xff09;Numba-GPU加速&#xff08;4&#xff09;Numba-CPU加速&#xff08;Z轴切块&#xff09;&#xff08;5&…

docker运行springboot项目

博客中若有侵权或者错误的地方&#xff0c;请及时告知&#xff0c;感谢。 1. 背景 在开发中使用k8s部署&#xff0c;日常也只是写个dockerFile, 没有想过整个部署流程是怎样的。今天我们自己部署docker镜像。 2.实战 2.1 建立springboot项目 (1) JAVA项目打包 (解决no mai…

Minio笔记-Centos搭建Minio

下载 Minio wget https://dl.min.io/server/minio/release/linux-amd64/minio 赋予执行权限 chmod x minio 创建存储目录 mkdir /data 运行 Minio ./minio server /data 默认端口为9000 访问 Minio 控制台&#xff1a;在浏览器中输入 http://your-server-ip:9000 默认…

FPGA Prototyping vs Emulation

FPGA Prototyping vs. Emulation One way to visualize the difference between Prototyping and Emulation is with a “spider chart” (named for its resemblance to a spider’s web). The Prototyping vs. Emulation spider chart below highlights the differences bet…

斐纳切数列考试题

计算机二级考试有一道题 result [] a,b0,1 while a<100:print(a,end,) a, b b, ab # 0,1,1,2,3,5,8,13,21,34,55,89,

LLM - 理解 多模态大语言模型 (MLLM) 的发展与相关技术 (二)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/142063880 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 多模态…

idea 日志打印乱码

在这里插入图片描述 配置中改为一致

算法篇_RGB图像数据压缩与解压(单片机使用)

文章目录 一、前言二、算法选型2.1 Run-Length Encoding (RLE)2.2 Differential Pulse-Code Modulation (DPCM) 三、采用RLE算法实现图像压缩四、哈夫曼编码实现压缩和解压4.1 哈夫曼编码压缩自定义数据与还原4.2 哈夫曼编码压缩完成图像的压缩和还原 书接上回&#xff08;上一…

Java重修笔记 第五十一天 泛型

泛型 1. 对加入集合的数据类型进行约束&#xff0c;提高了安全性 2. 不用做专门的数据类型转换&#xff0c;就可以直接使用从集合取出来的对象&#xff0c;效率高 在类定义中使用泛型 1. 在类名后面跟上 <泛型列表> 表示该类所使用的使用泛型&#xff0c;具体是什么…

nginx 使用篇 配置

一、介绍 1.介绍 Nginx是一个高性能的HTTP和反向代理服务器&#xff0c;同时也是一个邮件代理服务器&#xff0c;它以稳定性、丰富的功能集、简单的配置文件和低系统资源消耗而闻名。 作为一个轻量级的服务器&#xff0c;Nginx在处理高并发连接方面表现出色&#xff0c;能够支…

怎么修复松下相机死机视频只有0字节(0KB)的MDT文件【实测可修复】

死机后视频文件大小仅为0字节 松下S5相机录像死机&#xff0c;关机重新开机后有一个视频文件变成MDT&#xff0c;大小为0KB&#xff0c;录了30多分钟&#xff0c;本应为MOV格式的视频。0字节文件可以修复吗&#xff1f;怎么修复0字节的MDT文件为视频&#xff1f; 数据提取与视…

认知杂谈55

今天分享 有人说的一段争议性的话 I I I I 内容摘要 这篇内容主要有以下要点&#xff1a;首先&#xff0c;人际交往有难度&#xff0c;要让大家都喜欢很难&#xff0c;需学习沟通技巧&#xff0c;可通过看书、关注抖音博主、参加培训班及看罗翔视频片段来提升。其次&#xf…