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

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

目录

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; //1sfor (; 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.mrgr.cn/news/23177.html

相关文章:

  • 八、3 DMA数据转运(代码)
  • Linux内核的调试(TODO)
  • 【剑指offer】题解:和为S的两个数字
  • 【网络安全 | 渗透工具-目录FUZZ】ffuf安装使用详细教程
  • 【附源码】Python :斐波那契数列(10种方法计算第n项)
  • mysql——关于表的增删改查(CRUD)
  • 【880高数】高等数学一刷错题整理
  • 华为-2022-测试面试题
  • 概率论原理精解【12】
  • 机器人领域超重量奖项TRO傅京孙最佳论文奖汇总【上】
  • 每日一题,力扣leetcode Hot100之206反转链表
  • 【C++】C++入门基础,详细介绍命名空间,缺省参数,函数重载,引用,内联函数等
  • 11大排序的原理讲解和Python源码剖析
  • 【Kubernetes知识点问答题】监控与升级 / ETCD 备份与恢复
  • 田纳西州橡树岭全球最快的超级计算机名为Frontier
  • 【LabVIEW学习篇 - 18】:人机界面交互设计02
  • Lua协同程序Coroutine
  • HTTrack
  • 点餐小程序实战教程04变量定义和初始化
  • AI工具(不断更新)