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

定制营销型网站做网站被网监叫去很多次

定制营销型网站,做网站被网监叫去很多次,网页建站的费用,记事本可以做网站吗目录 设计思路 类的设计 模块实现 私有接口 公有接口 疑惑点 设计思路 poller 模块的意义主要体现在它对 epoll 进行了更高层次的封装,让开发者在监控文件描述符(比如 socket)的事件时,操作变得更简单、更直观。咱们可以从…

目录

设计思路

类的设计

模块实现

私有接口

公有接口

疑惑点


设计思路

poller 模块的意义主要体现在它对 epoll 进行了更高层次的封装,让开发者在监控文件描述符(比如 socket)的事件时,操作变得更简单、更直观。咱们可以从几个方面来看看它的价值:

简化复杂性
原生的 epoll 是 Linux 提供的一种高效 I/O 事件通知机制,但它的 API 使用起来稍微有点繁琐,比如需要手动创建 epoll 实例、注册事件、处理就绪事件等。poller 模块把这些底层操作封装成了更友好的接口,让你不用直接跟 epoll_ctl、epoll_wait 这些函数打交道,减少了出错的机会。
提高代码可读性
通过 poller,你可以更专注于业务逻辑,而不是陷入底层的事件管理细节。比如,注册一个描述符、指定关心的事件(比如可读、可写),poller 通常会提供简洁的方法,代码看起来更清晰,维护起来也更容易。
统一抽象
如果你的项目需要在不同平台上运行(比如 Linux 用 epoll,其他系统可能用 select 或 poll),poller 可以作为一个抽象层,屏蔽底层的差异,让你用一套统一的接口来处理事件监控。这对跨平台开发超级友好!

1. 设计 Poller 的核心功能

  • 创建和管理 epoll 实例
  • 添加/修改/删除文件描述符及其关心的事件
  • 等待并返回就绪的事件
  • 提供简单的事件循环接口
  • 基于这些需求,可以设计一个 Poller 类或者结构体,封装这些功能。 

2. 基本封装思路

  • 初始化:调用 epoll_create 创建一个 epoll 实例。
  • 添加描述符:用 epoll_ctl 注册文件描述符和事件。
  • 事件等待:用 epoll_wait 获取就绪的事件。
  • 销毁:关闭 epoll 实例

对于epoll模型,要怎么封装呢?

我们知道,一个EventLoop线程也就是一个从属Reactor线程需要对应一个 epoll 模型,也就是一个 Poller对象(因为Poller就是对epoll的封装),那么这个对象中要实现事件的监控,就必须要保存一个epoll操作句柄(就是钥匙,没有这个钥匙,启动不了对应的epoll),也就是一个_epfd。

对于epoll模型来说,当使用epoll_ctl(),把文件描述符添加到红黑树模型上之后,当有事件就绪了,操作系统会去红黑树上找到该就绪事件对应的文件描述符_fd。然后会把这些_fd放在就绪队列上。而我们的Poller模块就是封装的epoll,所以肯定会获取到就绪的事件。那么这些事件就需要存储到一个就绪队列中。我们使用一个 struct epoll_event 类型的数组来模拟就绪队列。但是对于Poller的上层,也就是EventLoop来说,它并不需要得到一个 struct epoll_event 的数组,他其实只需要知道哪些Channel 中的事件就绪了,然后调用 Channel中的 HandlerEvent 方法去处理就绪事件就行了,所以我们未来设置就绪事件获取的时候,返回给外界或者说外界不需要传 struct epoll_event 数组,但是我们内部调用 epoll_wait 又必须使用这个数组,那么我们其实可以在Poller对象内部定义一个 struct epoll_event 数组便于后续使用。

同时,我们需要对Poller模型中监控的文件描述符以及对应的Channel对象进行管理,一个fd对应一个Channel对象。因为未来我们在添加某个事件的监控时,有可能该文件描述符在之前我们并没有添加到 epoll 模型中,这时候我们使用的是 EPOLL_CTL_ADD 操作,而其他的时候,对监控的事件进行修改,则是EPOLL_CTL_MOD操作,仅仅是对epoll模型中的红黑树节点中的内容进行修改,并不对节点的删除与插入做管理。 

就比如在一个城市中,epfd就好比不同的饭馆_fd就对应进入饭馆的顾客,当一个顾客来吃饭,就把该顾客对应的信息注册到记录本上(比如一对情侣坐在了A桌上),然后此时服务员(Poller)对已经注册的顾客进行监控,当_fd就绪了,这时候就需要通过Channel模块了,因为Poller模块只是监听fd,并且把就绪的fd放到就绪队列上。至于这些就绪的事件接下来要干什么是要传给Channel中的回调函数处理的。就好比A顾客离开了(就绪了Close事件),然后服务员检测到了A顾客就绪了离开的事件,他就会在对讲机里跟老板说,老板知道了是A顾客离开了也就是知道了A顾客所在桌的路线(Channel),就会调用调用回调函数(保洁),去执行打扫卫生的工作。如果Poller和Channel不联系起来,那老板知道有顾客离开了,但是不知道是哪个顾客离开了。

类的设计

我们需要一个私有的实际进行epoll模型操作的结构,也就是上面的 Update 接口,同时也需要一个辅助接口,也就是IsInPoller ,来判断某个Channel是否已经在Poller的监控中,来决定是使用 EPOLL_CTL_ADD还是使用EPOLL_CTL_MOD操作。

class Poller
{
private:
#define REVENTS_SIZE 1024 int _epfd;      //epoll模型的操作句柄std::unordered_map<int,Channel*> _channels;  //保存管理的套接字以及对应的Channelstruct epoll_event _revents[REVENTS_SIZE];  //用于从epoll模型中获取就绪的文件描述符及其就绪事件然后存储里面
private://判断该文件描述符是否在epoll模型中bool HasChannel(Channel* channel); //判断文件描述符是否登记到了Channel中void Update(Channel* channel,int op); 更新文件描述符的事件
public:             //提供的接口void UpdateEvents(Channel* channel); //添加/更新事件监控void Remove(Channel*channel); //删除事件监控size_t PollCtl(vector<Channel*> *active); //就绪事件放到"就绪队列"中
};

模块实现

私有接口

    bool HasChannel(Channel* channel) //判断文件描述符是否登记到了Channel中{return _channels.find(channel->Fd()) == _channels.end() ? false : true;}void Update(Channel* channel, int op) //更新文件描述符的事件{int fd = channel->Fd();struct epoll_event ev;ev.data.fd = fd;ev.events = channel->Events();int ret = epoll_ctl(_epfd, op, fd, &ev);if(ret < 0){ERR_LOG("epoll_ctl error");}return;}

公有接口

在公有接口中,有个函数 PollCtl 比较难理解,这里我给大家单独讲解一下。这个函数的目的是检测有哪些事件就绪(比如 socket 可读、可写),然后把对应的 Channel 塞进 actives 中,交给上层(比如 EventLoop)去处理类比epoll模型的就绪队列

    size_t PollCtl(vector<Channel*> *active)//就绪事件放到"就绪队列"中{int cnt = epoll_ctl(_epfd, _revents, REVENTS_SIZE, -1);//阻塞等待if(cnt < 0){if(errno = EINTR)ERR_LOG("epoll_wait failed");abort();}for(int i = 0; i <cnt; i++){auto it = _channels.find(_revents[i].data.fd);if(it == _channels.end()){ERR_LOG("find channel failed");abort();}it->second->SetRevents(_revents[i].events);active->push_back(it->second);}return cnt;}

完整公有接口

public:Poller(){_epfd = epoll_create(1);if(_epfd < 0){ERR_LOG("epoll_create error");abort(); //退出程序}}void UpdateEvents(Channel* channel)//添加/更新事件监控{if(HasChannel(channel))  //检查下是否已经在红黑树模型上{Update(Channel* channel, EPOLL_CTL_MOD);return;}_channels.insert(std::make_pair(channel->Fd(), channel)); //添加到红黑树模型上Update(channel, EPOLL_CTL_ADD);}size_t PollCtl(vector<Channel*> *active)//就绪事件放到"就绪队列"中{int cnt = epoll_ctl(_epfd, _revents, REVENTS_SIZE, -1);//阻塞等待if(cnt < 0){if(errno = EINTR)ERR_LOG("epoll_wait failed");abort();}for(int i = 0; i <cnt; i++){auto it = _channels.find(_revents[i].data.fd);if(it == _channels.end()){ERR_LOG("find channel failed");abort();}it->second->SetRevents(_revents[i].events);active->push_back(it->second);}return cnt;}void RemoveEvent(Channel* channel)//删除事件监控{auto it = _channels.find(channel->Fd());if(it ! = _channels.end()){_channels.erase(it);Update(channel, EPOLL_CTL_DEL);}}
};

目前来说,这段代码编译起来没什么问题,等后续我们实现完EventLoop模块再来进行联合调试。实现完EventLoop之后,我们的逻辑就大概清晰了。

疑惑点

std::unordered_map<int,Channel*> _channels;为啥需要这个

 那为啥map中的值不是Channel而是Channel*呢?

auto it = _channels.find(_revents[i].data.fd); 这个返回的是什么?这个it是个unorder_map?

active->push_back(it->second);这个是干嘛的,这个不是就绪队列吗,不是应该把就绪事件的文件描述符传给这个vector队列吗?怎么把channel*对象指针给填进去?

这个channel对象里面既有文件描述符也还有该文件描述符所对应的就绪事件,以及对应的回调函数

http://www.dtcms.com/a/550928.html

相关文章:

  • 东莞网站包年优化一站式网站建设平台
  • 网站如何做360优化小程序开发费用一览表
  • 302-Spring AI Alibaba MCP NL2SQL 示例
  • 网站音频播放器代码一个平台怎么推广
  • Rust 过程宏开发入门:元编程的艺术与实践
  • 热 动漫-网站正在建设中-手机版6网站上的字体大小
  • 长沙建网站公司网上商城电商项目
  • 网站的主要功能网站建设分几种编程语言
  • 网站编辑做seo好做吗网络推广的公司
  • 工程化(八股)
  • 百度云搭建网站商城网站的建设方案
  • 网站做电商资质吗西安有没有网站建设和营销的培训
  • 汕头模板自助建站叫别人做网站要注意什么
  • html5网站建设基本流程seo排名查询工具
  • 网站系统管理百度推广培训机构
  • 深圳万齐网站建设2020网络公司排名
  • 好未来披露Q2财报:营收8.61亿美元,净利润1.24亿美元
  • Java基础复习-中-集合
  • 【数据库】约束
  • 黄浦网站设计北京网站改版报价
  • 苏州企业网站制作服务wordpress 看不到主题
  • 心率血氧传感器介绍
  • Rust智能指针的奇妙之旅:从踩坑到顿悟
  • 鹰潭做网站的有什么网站是做平面设计的
  • 网站开发有哪些流程Wordpress 插件开发者
  • 公司网站一年多少钱苏州网站制作网络建设公司
  • 赣州网站建设哪家公司好wordpress后台登录不上
  • 服装公司电商网站建设规划建设银行网站最近都打不开吗
  • 浅谈 BSGS(Baby-Step Giant-Step 大步小步)算法
  • 大石网站建设做ppt找图片在哪个网站好