基于Reactor模型的高性能网络库之Poller(EpollPoller)组件

简介: 封装底层 I/O 多路复用机制(如 epoll)的抽象类 Poller,提供统一接口支持多种实现。Poller 是一个抽象基类,定义了 Channel 管理、事件收集等核心功能,并与 EventLoop 绑定。其子类 EPollPoller 实现了基于 epoll 的具体操作,包括事件等待、Channel 更新和删除等。通过工厂方法可创建默认的 Poller 实例,实现多态调用。

封装底层 I/O 多路复用机制epoll )的 抽象类 Poller

  • Poller 是一个抽象基类(有纯虚函数)。

功能

说明

IO复用抽象层

提供统一接口,支持 epoll、poll 等底层实现

Channel 管理

注册、更新、删除所有监听的 Channel

活跃事件收集

收集并返回所有有事件的 Channel 供 EventLoop 分发

与 EventLoop 绑定

每个 Poller 服务于一个 EventLoop



class    Poller: noncopyable
{
public:
using  ChannelList=std::vector<Channel*>;//定义一个别名 ChannelList,表示当前活跃的 Channel* 列表,用于事件分发
    Poller(EventLoop*loop);//每个 Poller 实例都只为一个 EventLoop 服务
    virtual  ~Poller()=default;//虚析构函数,支持多态析构,释放派生类资源(比如 EPollPoller)。
    

    //给所有IO复用保留统一接口

//核心接口,等待 IO 事件的发生(如调用 epoll_wait()),超时时间为 timeoutMs 毫秒。
当有事件发生时,将活跃的 Channel* 填入 activeChannels 中。
返回值是当前时间戳(Timestamp 一般是 typedef 的封装类型)。
    virtual Timestamp  poll(int timeoutMS,ChannelList*activeChannels )=0;
    //向 Poller 注册或更新一个 Channel 的事件(如监听可读、可写事件)。
    virtual void   updateChannel(Channel *channel)=0;
    //将某个 Channel 从 Poller 中移除。
    virtual void   removeChannel(Channel *channel)=0;
    //判断一个poller是否有channel
    bool hasChannel(Channel   *channel)const;

    //拿到默认的一个io复用处理
    //工厂方法,根据系统默认配置创建一个合适的 Poller 实例(如使用 epoll)
    static  Poller*newDefaultPoller(EventLoop *Loop);
protected:
//key: sockfd       value:sockfd所属的channel通道类型
//key 是 fd,value 是对应的 Channel*。
//管理所有注册到 Poller 的 Channel,方便更新、查询、删除。
    using  ChannelMap=std::unordered_map<int,Channel*>;
    ChannelMap  channels_;
private:
    EventLoop* ownerLoop_;//定义poller所属的事件循环EventLoop;
};
//作用是初始化 Poller,把它绑定到所属的 EventLoop 对象上,方便 Poller 在需要时访问 EventLoop 的信息。
Poller::Poller(EventLoop  *loop):ownerLoop_(loop)
{
}

// 判断 Poller 是否管理某个 Channel
//返回 true 表示这个 Poller 正在管理(监听)这个 Channel 对应的 fd
bool Poller::hasChannel(Channel   *channel)const
{
    auto  it=channels_.find(channel->fd());
    return   it!=channels_.end()&&it->second==channel;
}

//静态工厂方法 
//对调用者来说,只需调用 Poller::newDefaultPoller(loop),得到一个指向 Poller 抽象基类的指针,不用关心是 PollPoller 还是 EPollPoller。
//返回值是 Poller*,即抽象基类指针,但实际指向的是具体子类实例,满足多态调用。 
  Poller*Poller::newDefaultPoller(EventLoop *Loop)
   {
    if(::getenv("MUDUO_USE_POLL"))
    {
        return   nullptr;//生成poll的实例
    }
    else
    {
        return new EPollPoller(Loop);//生成epoll的实例
    }
   }

子类(EpollPoller)


功能

说明

管理 Channel 与 fd 的映射

std::unordered_map<int, Channel*> channels_

负责 epoll 的创建与销毁

构造函数中创建 epollfd_

,析构时关闭

实现三大核心接口:

- poll()

:调用 epoll_wait

,返回活跃事件的 Channel 列表
-
updateChannel()

:通过 epoll_ctl

添加/修改监听的 fd
-
removeChannel()

:从 epoll 中删除 fd

提供 fillActiveChannels()

epoll_wait

返回的事件,转换为活跃的 Channel

列表,供 EventLoop

使用

使用 epoll 高效监听海量连接

相比 poll/select

epoll

支持边缘触发、O(1) 事件分发,不受 FD 上限影响

成员变量

//在调用 epoll_wait 时需要一个事件列表缓冲区,这里用 events_ 来存储活跃的 epoll_event,初始空间预留16个事件。
  static  const   int KInitEventListSize=16;
    //更新channel通道
    using   EventList=std::vector<epoll_event>;//用于存储 epoll_wait 返回的活跃事件数组。
    int epollfd_;存储 epoll 实例的文件描述符。
    EventList  events_;//保存活跃事件的数组。

const   int  KNew=-1;//channel未添加到poller    channel成员变量index -1
const   int    KAdded=1;//channel已添加到poller
const   int    KDeleted=2;//channel从poller删除

成员方法实现

构造函数
EPollPoller::EPollPoller(EventLoop *loop):
Poller(loop),           // 调用基类 Poller 的构造函数,保存所属 EventLoop
epollfd_(::epoll_create1(EPOLL_CLOEXEC))//当调用 exec 系列函数时自动关闭该 fd。
,events_(KInitEventListSize) // 初始化事件数组大小为 16(默认值)
{
    if(epollfd_<0)//// 创建失败则日志致命错误,程序终止
    {
        LOG_FATAL("epoll_create error:%d\n",errno);
    }
}

析构函数
EPollPoller::~EPollPoller()
{
    ::close(epollfd_);//当 EPollPoller 对象销毁时,关闭内部使用的 epoll 文件描述符(epollfd_),释放内核资源。
}




//该函数是 epoll 的 wait() 封装 —— 等待事件发生,并将活跃的 Channel 填充到 activeChannels 中返回。
Timestamp EPollPoller::poll(int timeoutMS,ChannelList  *activeChannels)
  {
        //打印日志:当前 poll 操作中,Poller 管理了多少个 fd(即 Channel 数量)
        LOG_INFO("func=%s=> fd total  count:%lu\n",__FUNCTION__,channels_.size());
        //返回值是触发的事件数量
        //events_用于保存触发事件的 epoll_event
        //&*events_.begin(): 拿到底层数组首地址
        int  numEvents=::epoll_wait(epollfd_,&*events_.begin(),static_cast<int>(events_.size()),timeoutMS);

        //立即保存 errno,因为接下来会调用其他系统或日志函数,可能会覆盖它。
        int saveError=errno;
        // 获取当前时间戳,代表“这批事件是何时触发的”。
        Timestamp  now(Timestamp::now());
         
        if(numEvents>0)// 有事件触发
        {
            LOG_INFO("%d events  happened \n",numEvents);
            //触发的 epoll_event 转换为 Channel 指针,写入到 *activeChannels 中,以便 EventLoop 进一步调用回调处理。
            fillActiveChannels(numEvents,activeChannels);
            // 如果本轮触发的事件数量等于 events_ 当前容量,说明缓冲区满了,需要扩容(加倍策略),为下次 epoll_wait 提前分配空间。
            if(numEvents==events_.size())
            {
                events_.resize(events_.size()*2);
            }
        }// 没有事件触发(超时)
        else  if(numEvents==0)
        {
            LOG_DEBUG("%s  timeout!\n",__FUNCTION__);
        }
        else// epoll_wait 返回负数,说明出错了
        {
            if(saveError!=EINTR)
            {
                errno=saveError;
                LOG_ERROR("EpollPoller::poll() error!");
            }
        }
        return now; //  正确返回一个 Timestamp 对象
  }



void   EPollPoller::updateChannel(Channel  *channel)//将 Channel 添加、修改或删除到 epoll 实例 
{
    //根据 Channel 的当前状态(index)决定是否调用 epoll_ctl 的 ADD / MOD / DEL,并同步更新内部状态。
    //获取当前 Channel 的状态索引(可能是 KNew / KAdded / KDeleted),用于判断如何处理该 channel
    const int  index=channel->index();
    // 打印日志,查看当前是哪个 channel、fd 是多少、感兴趣的事件是什么、当前状态是啥。
    LOG_INFO("fuc=%s=> fd=%d   events=%d   index=%d \n",__FUNCTION__,channel->fd(),channel->events(),index);

   // 如果当前 channel 是“新注册”或者“已经从 epoll 删除了但仍存在”,都需要重新通过 EPOLL_CTL_ADD 加入 epoll。
    if(index==KNew||index==KDeleted)
    {
        if(index==KNew)// 如果是全新的 channel(index == -1),将其注册到内部的 channels_ 哈希表中,便于后续通过 fd 找到对应的 Channel。
        {
            int fd=channel->fd();
            channels_[fd]=channel;
        }
        channel->setindex(KAdded);//无论是新建还是重新添加,都统一标记为 KAdded
        update(EPOLL_CTL_ADD,channel);//调用封装的 update 方法,用 epoll_ctl(epollfd_, EPOLL_CTL_ADD, ...) 真正注册到内核 epoll 实例
    }
    else//说明这个 channel 已经在 epoll 中注册过(index == KAdded)
    {
        int fd=channel->fd();// 取出该 channel 的 fd,用于日志或后续判断。
        if(channel->isNoneEvent())// 如果该 channel 当前没有关注任何事件(即 events == 0)
        //从 epoll 实例中注销(epoll_ctl DEL),并将 index 状态改为 KDeleted
        {
            update(EPOLL_CTL_DEL,channel);
            channel->setindex(KDeleted);
        }
        else// 如果仍然对某些事件感兴趣(如 EPOLLIN/EPOLLOUT),则更新内核中该 fd 的监听事件类型(epoll_ctl MOD)
        {
            update(EPOLL_CTL_MOD,channel);
        }
    }


//:从 Poller(epoll 封装)中移除一个 Channel,包括:
 channels_ 映射中移除;
 epoll 实例中注销(如果已注册)
 void   EPollPoller::removeChannel(Channel *channel)//epoll_ctr->del    //从poller中删除channel
    {
            int fd=channel->fd();
            int  index=channel->index();
             channels_.erase(fd);  // 从内部的 ChannelMap(std::unordered_map<int, Channel*> channels_)中删除该 channel。
              LOG_INFO("fuc=%s=> fd=%d \n",__FUNCTION__,fd);
             if(index==KAdded)//如果该 channel 当前确实已经注册到了 epoll 实例
             {
                update(EPOLL_CTL_DEL,channel);//在epoll中注册过,要在epoll中删除
             } 
             channel->setindex(KNew);//把 channel 的状态设置为 KNew(即未加入 epoll 的状态)
   }


//根据 epoll_wait() 返回的事件列表,找到对应的 Channel 对象,设置其触发的事件类型,并将其加入到 EventLoop 的活跃列表中(activeChannels)。
   void   EPollPoller::fillActiveChannels(int   numEvents,ChannelList  *activeChannels)const
     {
        for(int i=0;i<numEvents;++i)//遍历 epoll_wait 返回的每一个活跃事件(epoll_event);
        {
          //从 epoll_event.data.ptr 中恢复出原始注册时的 Channel*
            Channel *channel=static_cast<Channel*>(events_[i].data.ptr);
            LOG_INFO("epoll_wait result: event[%d] channel=%p events=%d\n", i, channel, events_[i].events);
          //设置 channel 的 实际发生事件(revents)
            channel->set_revents(events_[i].events);
          //将该活跃 channel 加入到 EventLoop 的活跃列表中
            activeChannels->push_back(channel);//Eventloop拿到了他的poller返回得所有发生事件得channel列表了
        }
     }


//将某个 Channel 对象对应的 fd 及其感兴趣事件通过 epoll_ctl 注册、修改或删除到 epoll 实例中
//operation 是 epoll_ctl 的操作类型:
EPOLL_CTL_ADD:添加新的 fd;
EPOLL_CTL_MOD:修改已有的 fd;
EPOLL_CTL_DEL:删除 fd。
void   EPollPoller::update(int operation,Channel*channel)
    {
        epoll_event  event;//创建一个 epoll_event 结构体,用于告诉 epoll 要监听哪些事件;
        memset(&event,0,sizeof  event);//使用 memset 清零,保证结构体初始状态干净
          int fd=channel->fd();
        event.events=channel->events();//设置 epoll_event 的事件类型(如 EPOLLIN, EPOLLOUT 等),这些事件来自 channel->enableReading() 等方法设置的 events_ 成员。
        event.data.ptr=channel;//将 channel 的地址存入 epoll_event 的 data.ptr 字段;
        LOG_INFO("epoll_ctl op=%d fd=%d channel=%p\n", operation, fd, channel);

      //调用系统函数 epoll_ctl 将 fd 与事件注册到 epoll 实例中
        if(::epoll_ctl(epollfd_,operation,fd,&event)<0)
        {
            if(operation==EPOLL_CTL_DEL)//如果是删除操作出错,打印错误日志
            {
                LOG_ERROR("epoll_ctl  del  error:%d\n",errno);
            }
             else//自动exit
            {
                LOG_FATAL("epoll_ctl  add/mod  error:%d\n",errno);
            }
        }

    }
   
     
相关文章
|
2月前
|
网络协议 算法 Java
基于Reactor模型的高性能网络库之Tcpserver组件-上层调度器
TcpServer 是一个用于管理 TCP 连接的类,包含成员变量如事件循环(EventLoop)、连接池(ConnectionMap)和回调函数等。其主要功能包括监听新连接、设置线程池、启动服务器及处理连接事件。通过 Acceptor 接收新连接,并使用轮询算法将连接分配给子事件循环(subloop)进行读写操作。调用链从 start() 开始,经由线程池启动和 Acceptor 监听,最终由 TcpConnection 管理具体连接的事件处理。
60 2
|
2月前
|
JSON 监控 网络协议
干货分享“对接的 API 总是不稳定,网络分层模型” 看电商 API 故障的本质
本文从 OSI 七层网络模型出发,深入剖析电商 API 不稳定的根本原因,涵盖物理层到应用层的典型故障与解决方案,结合阿里、京东等大厂架构,详解如何构建高稳定性的电商 API 通信体系。
|
2月前
基于Reactor模式的高性能网络库github地址
https://github.com/zyi30/reactor-net.git
48 0
|
26天前
|
算法 安全 网络安全
【多智能体系统】遭受DoS攻击的网络物理多智能体系统的弹性模型预测控制MPC研究(Simulink仿真实现)
【多智能体系统】遭受DoS攻击的网络物理多智能体系统的弹性模型预测控制MPC研究(Simulink仿真实现)
|
1月前
|
运维 监控 安全
计算机网络及其安全组件纲要
本文主要介绍了 “计算机网络及常见组件” 的基本概念,涵盖网卡、IP、MAC、OSI模型、路由器、交换机、防火墙、WAF、IDS、IPS、域名、HTTP、HTTPS、网络拓扑等内容。
188 0
|
9月前
|
SQL 安全 网络安全
网络安全与信息安全:知识分享####
【10月更文挑战第21天】 随着数字化时代的快速发展,网络安全和信息安全已成为个人和企业不可忽视的关键问题。本文将探讨网络安全漏洞、加密技术以及安全意识的重要性,并提供一些实用的建议,帮助读者提高自身的网络安全防护能力。 ####
219 17
|
9月前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将从网络安全漏洞、加密技术和安全意识三个方面进行探讨,旨在提高读者对网络安全的认识和防范能力。通过分析常见的网络安全漏洞,介绍加密技术的基本原理和应用,以及强调安全意识的重要性,帮助读者更好地保护自己的网络信息安全。
171 10
|
9月前
|
存储 SQL 安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将介绍网络安全的重要性,分析常见的网络安全漏洞及其危害,探讨加密技术在保障网络安全中的作用,并强调提高安全意识的必要性。通过本文的学习,读者将了解网络安全的基本概念和应对策略,提升个人和组织的网络安全防护能力。
|
9月前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
在数字化时代,网络安全和信息安全已成为我们生活中不可或缺的一部分。本文将介绍网络安全漏洞、加密技术和安全意识等方面的内容,并提供一些实用的代码示例。通过阅读本文,您将了解到如何保护自己的网络安全,以及如何提高自己的信息安全意识。
175 10
|
9月前
|
监控 安全 网络安全
网络安全与信息安全:漏洞、加密与意识的交织
在数字时代的浪潮中,网络安全与信息安全成为维护数据完整性、保密性和可用性的关键。本文深入探讨了网络安全中的漏洞概念、加密技术的应用以及提升安全意识的重要性。通过实际案例分析,揭示了网络攻击的常见模式和防御策略,强调了教育和技术并重的安全理念。旨在为读者提供一套全面的网络安全知识框架,从而在日益复杂的网络环境中保护个人和组织的资产安全。

热门文章

最新文章