仿Muduo的高并发服务器:Channel模块与Poller模块

本期我们接着来深入项目

相关代码已经上传gitee:仿muduo服务器: 本项目致力于实现一个仿造muduo库的简易并发服务器,为个人项目,参考即可喜欢请点个赞,谢谢

目录

Channel模块

总体设计

源码

Poller模块

设计模块

源码

Channel与Poller联调


Channel模块

总体设计

目的:对描述符的监控事件管理

功能

1.事件管理:

描述符是否可读

描述符是否可写

对描述符监控可读

对描述符监控可写

解除可读事件监控

解除可写事件监控

解除所有事件监控

而以上的事件都是一个数值uint32_t进行保存

2.事件触发后的处理的管理

当前需要监控的事件

1.需要处理的事件:可读,可写,挂断,错误,任意

2.事件处理的回调函数

成员

后边使用epoll进行事件监控,要进行事件管理,就需要有一个uint32_t类型的成员保存当前需要监控的事件

EPOLLIN可读

EPOLLOWT可写

EPOLLRDHUP连接断开

EPOLLPRI优先数据

EPOLLERR出错了

EPOLLHUP挂断

事件处理这里,因为有五种事件需要处理,就需要五个回调函数

源码

Channel.hpp

cpp 复制代码
#pragma once
#include <cstdint>
#include <functional>
#include <poll.h>
namespace ImMuduo 
{
    class Channel
    {
        using EventCallback = std::function<void()>;

        public:
            Channel(int fd);
            ~Channel()=default;
            //获取文件描述符
            int GetFd()const;
            //获取当前需要监控的事件
            uint32_t GetEvents()const;
            //获取当前事件发生时的事件
            uint32_t GetRevents()const;
            //设置当前实际触发的事件(由 Poller 调用)
            void SetRevents(uint32_t revents);
            //设置可读事件回调函数
            void SetReadCallback(const EventCallback& cb);
            //设置可写事件回调函数
            void SetWriteCallback(const EventCallback& cb);
            //设置错误事件回调函数
            void SetErrorCallback(const EventCallback& cb);
            //设置关闭事件回调函数
            void SetCloseCallback(const EventCallback& cb);
            //设置任意事件回调函数
            void SetEventCallback(const EventCallback& cb);
            //判断是否监控了可读
            bool ReadAble()const;
            //判断是否监控了可写
            bool WriteAble()const;
            //开启可读事件
            void EnableRead();
            //关闭可读事件
            void DisableRead();
            //开启可写事件
            void EnableWrite();
            //关闭可写事件
            void DisableWrite();
            //关闭所有事件
            void DisableAll();
            //移除监控并清除信息
            void Remove();
            //事件管理------一旦触发这个事件就调用该函数
            void HandleEvent();
        private:
            int fd_;//文件描述符
            uint32_t events_;//当前需要监控的事件
            uint32_t revents_;//当前事件发生时的事件
            EventCallback readCallback_;//可读事件回调函数
            EventCallback writeCallback_;//可写事件回调函数
            EventCallback errorCallback_;//错误事件回调函数
            EventCallback closeCallback_;//关闭事件回调函数
            EventCallback eventCallback_;//任意事件回调函数
    };
}

Channel.cpp

cpp 复制代码
#include "Channel.hpp"
#include <poll.h>
namespace ImMuduo
{
    Channel::Channel(int fd)
    :fd_(fd), events_(0), revents_(0)
    {}
    int Channel::GetFd()const
    {
        return fd_;
    }
    uint32_t Channel::GetEvents()const
    {
        return events_;
    }
    uint32_t Channel::GetRevents()const
    {
        return revents_;
    }

    void Channel::SetRevents(uint32_t revents)
    {
        revents_ = revents;
    }

    void Channel::SetReadCallback(const EventCallback& cb)
    {
        readCallback_=cb;
    }
    void Channel::SetWriteCallback(const EventCallback& cb)
    {
        writeCallback_=cb;
    }   
    void Channel::SetErrorCallback(const EventCallback& cb)
    {
        errorCallback_=cb;
    }
    void Channel::SetCloseCallback(const EventCallback& cb)
    {
        closeCallback_=cb;
    }
    void Channel::SetEventCallback(const EventCallback& cb)
    {
        eventCallback_=cb;
    }
    bool Channel::ReadAble()const
    {
        return (events_&POLLIN);
    }
    bool Channel::WriteAble()const
    {
        return (events_ & POLLOUT);
    }

    void Channel::EnableRead()
    {
        events_ |= POLLIN;
    }

    void Channel::DisableRead()
    {
        events_ &= ~POLLIN;
    }

    void Channel::EnableWrite()
    {
        events_ |= POLLOUT;
    }

    void Channel::DisableWrite()
    {
        events_ &= ~POLLOUT;
    }

    void Channel::DisableAll()
    {
        events_ = 0;
    }

    void Channel::Remove()
    {
        events_ = 0;
        revents_ = 0;
        readCallback_ = nullptr;
        writeCallback_ = nullptr;
        errorCallback_ = nullptr;
        closeCallback_ = nullptr;
        eventCallback_ = nullptr;
    }

    void Channel::HandleEvent()
    {
        // 挂起事件(对端关闭),且没有可读数据时才处理
        if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
        {
            if (closeCallback_) closeCallback_();
        }

        // 错误事件
        if (revents_ & (POLLERR | POLLNVAL))
        {
            if (errorCallback_) errorCallback_();
        }

        // 可读事件(普通数据、紧急数据、对端半关闭)
        if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))
        {
            if (readCallback_) readCallback_();
        }

        // 可写事件
        if (revents_ & POLLOUT)
        {
            if (writeCallback_) writeCallback_();
        }

        // 通用事件回调
        if (eventCallback_) eventCallback_();
    }
}

Poller模块

设计模块

作用 :描述符IO事件监控模块

意义:通过epoll实现对描述符的IO事件监控

功能

  1. 添加/修改描述符的事件监控(不存在则添加,存在则修改)

  2. 移除描述符的事件监控

封装思想

  1. 必须拥有一个epoll的操作句柄

  2. 拥有一个struct epoll_event结构数组,监控时保存所有的活跃事件

  3. 使用hash表管理描述符与描述符对应的事件管理Channel对象

逻辑流程

  1. 对描述符进行监控,通过Channel才能知道描述符需要监控什么事件

  2. 当描述符就绪了,通过描述符在hash表中找到对应的Channel(得到了Channel才能知道什么事件如何处理)

当描述符就绪了,返回就绪描述符对应的Channel

源码

Poller.hpp

cpp 复制代码
#pragma once
#include "Channel.hpp"
#include <vector>
#include <unordered_map>
#include <sys/epoll.h>
namespace ImMuduo
{
    const int kInitEventSize = 1024; //初始事件数组大小 
    class Poller
    {
        public:
            Poller() noexcept;
            ~Poller()=default;
            //添加修改监控通道
            void UpdateChannel(Channel* channel);
            //移除监控通道并清除信息
            void RemoveChannel(Channel* channel);
            //开始监控,返回活跃通道
            void Poll(std::vector<Channel*>& activeChannels);
        private:
            //实际上对epoll的操作
            void Update(Channel* channel,int op);
            //判断是否监控了该通道
            bool HasChannel(Channel* channel) const;

        private:
            int epfd_;
            //事件数组
            struct epoll_event events_[kInitEventSize];
            std::unordered_map<int,Channel*> channels_;
    };
}

Poller.cpp

cpp 复制代码
#include "Poller.hpp"
#include "Log.hpp"
#include <cstring>
namespace ImMuduo
{
    Poller::Poller() noexcept
    {
        // epoll_create1 的参数是 flags,不是 size,用 0 即可
        epfd_ = epoll_create1(0);
        if (epfd_ < 0)
        {
            ERROR("epoll_create1 Failed: %s", strerror(errno));
            std::abort();
        }
    }

    void Poller::UpdateChannel(Channel* channel)
    {
        bool exists = HasChannel(channel);
        if (!exists)
        {
            // 不存在就添加
            channels_[channel->GetFd()] = channel;
            Update(channel, EPOLL_CTL_ADD);
        }
        else
        {
            // 存在就修改
            Update(channel, EPOLL_CTL_MOD);
        }
    }

    void Poller::RemoveChannel(Channel* channel)
    {
        auto it = channels_.find(channel->GetFd());
        if (it != channels_.end())
        {
            channels_.erase(it);
            Update(channel, EPOLL_CTL_DEL);
        }
    }

    void Poller::Poll(std::vector<Channel*>& activeChannels)
    {
        activeChannels.clear();
        int numEvents = epoll_wait(epfd_, events_, kInitEventSize, -1);
        if (numEvents < 0)
        {
            if (errno != EINTR)
            {
                ERROR("epoll_wait Failed: %s", strerror(errno));
                return;
            }
            ERROR("epoll_wait EINTR: %s", strerror(errno));
            return;
        }
        activeChannels.reserve(numEvents);
        for (int i = 0; i < numEvents; ++i)
        {
            auto it = channels_.find(events_[i].data.fd);
            assert(it != channels_.end());
            it->second->SetRevents(events_[i].events);
            activeChannels.push_back(it->second);
        }
    }

    bool Poller::HasChannel(Channel* channel) const
    {
        auto it = channels_.find(channel->GetFd());
        return it != channels_.end();
    }

    void Poller::Update(Channel* channel, int op)
    {
        int fd = channel->GetFd();
        struct epoll_event event;
        event.data.fd = fd;
        event.events = channel->GetEvents();
        int ret = epoll_ctl(epfd_, op, fd, &event);
        if (ret < 0)
        {
            ERROR("epoll_ctl Failed: %s", strerror(errno));
            std::abort();
        }
    }
}

Channel与Poller联调

有了Poller模块后,我们可以对Channel进行修正

Channel.hpp

cpp 复制代码
#pragma once
#include <cstdint>
#include <functional>
#include <poll.h>
namespace ImMuduo
{
    class Poller;  // 前向声明,避免循环依赖
    class Channel
    {
        using EventCallback = std::function<void()>;

        public:
            Channel(int fd,Poller* poller);
            ~Channel()=default;
            //获取文件描述符
            int GetFd()const;
            //获取当前需要监控的事件
            uint32_t GetEvents()const;
            //获取当前事件发生时的事件
            uint32_t GetRevents()const;
            //设置当前实际触发的事件(由 Poller 调用)
            void SetRevents(uint32_t revents);
            //设置可读事件回调函数
            void SetReadCallback(const EventCallback& cb);
            //设置可写事件回调函数
            void SetWriteCallback(const EventCallback& cb);
            //设置错误事件回调函数
            void SetErrorCallback(const EventCallback& cb);
            //设置关闭事件回调函数
            void SetCloseCallback(const EventCallback& cb);
            //设置任意事件回调函数
            void SetEventCallback(const EventCallback& cb);
            //判断是否监控了可读
            bool ReadAble()const;
            //判断是否监控了可写
            bool WriteAble()const;
            //开启可读事件
            void EnableRead();
            //关闭可读事件
            void DisableRead();
            //开启可写事件
            void EnableWrite();
            //关闭可写事件
            void DisableWrite();
            //关闭所有事件
            void DisableAll();
            //移除监控并清除信息
            void Remove();
            //更新监控通道
            void Update();
            //事件管理------一旦触发这个事件就调用该函数
            void HandleEvent();
        private:
            int fd_;//文件描述符
            Poller* poller_;//所属的Poller
            uint32_t events_;//当前需要监控的事件
            uint32_t revents_;//当前事件发生时的事件
            EventCallback readCallback_;//可读事件回调函数
            EventCallback writeCallback_;//可写事件回调函数
            EventCallback errorCallback_;//错误事件回调函数
            EventCallback closeCallback_;//关闭事件回调函数
            EventCallback eventCallback_;//任意事件回调函数
    };
}

Channel.cpp

cpp 复制代码
#include "Channel.hpp"
#include "Poller.hpp"
#include <poll.h>
namespace ImMuduo
{
    Channel::Channel(int fd,Poller* poller)
    :fd_(fd), poller_(poller), events_(0), revents_(0)
    {}
    int Channel::GetFd()const
    {
        return fd_;
    }
    uint32_t Channel::GetEvents()const
    {
        return events_;
    }
    uint32_t Channel::GetRevents()const
    {
        return revents_;
    }

    void Channel::SetRevents(uint32_t revents)
    {
        revents_ = revents;
    }

    void Channel::SetReadCallback(const EventCallback& cb)
    {
        readCallback_=cb;
    }
    void Channel::SetWriteCallback(const EventCallback& cb)
    {
        writeCallback_=cb;
    }   
    void Channel::SetErrorCallback(const EventCallback& cb)
    {
        errorCallback_=cb;
    }
    void Channel::SetCloseCallback(const EventCallback& cb)
    {
        closeCallback_=cb;
    }
    void Channel::SetEventCallback(const EventCallback& cb)
    {
        eventCallback_=cb;
    }
    bool Channel::ReadAble()const
    {
        return (events_&POLLIN);
    }
    bool Channel::WriteAble()const
    {
        return (events_ & POLLOUT);
    }

    void Channel::EnableRead()
    {
        events_ |= POLLIN;
        Update();
    }

    void Channel::DisableRead()
    {
        events_ &= ~POLLIN;
        Update();
    }

    void Channel::EnableWrite()
    {
        events_ |= POLLOUT;
        Update();
    }

    void Channel::DisableWrite()
    {
        events_ &= ~POLLOUT;
        Update();
    }

    void Channel::DisableAll()
    {
        events_ = 0;
        Update();
    }

    void Channel::Remove()
    {
        poller_->RemoveChannel(this);
        events_ = 0;
        revents_ = 0;
        readCallback_ = nullptr;
        writeCallback_ = nullptr;
        errorCallback_ = nullptr;
        closeCallback_ = nullptr;
        eventCallback_ = nullptr;
    }

    void Channel::Update()
    {
        if (poller_) poller_->UpdateChannel(this);
    }

    void Channel::HandleEvent()
    {
        // 挂起事件(对端关闭),且没有可读数据时才处理
        if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
        {
            if (closeCallback_) closeCallback_();
        }

        // 错误事件
        if (revents_ & (POLLERR | POLLNVAL))
        {
            if (errorCallback_) errorCallback_();
        }

        // 可读事件(普通数据、紧急数据、对端半关闭)
        if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))
        {
            if (readCallback_) readCallback_();
        }

        // 可写事件
        if (revents_ & POLLOUT)
        {
            if (writeCallback_) writeCallback_();
        }

        // 通用事件回调
        if (eventCallback_) eventCallback_();
    }
}

本期内容到这里就结束了,喜欢请关注以下,谢谢

封面图自取:

相关推荐
zhouwy1131 小时前
Linux网络编程从入门到精通
linux·c++
迷途之人不知返1 小时前
deque的简单认识
数据结构·c++
知识分享小能手1 小时前
R语言入门学习教程,从入门到精通,R语言网络关系数据可视化(8)
学习·信息可视化·r语言
xieliyu.1 小时前
Java手搓数据结构:栈与队列模拟实现
java·数据结构·学习
zhangrelay1 小时前
ROS Kinetic-信号与系统-趣味案例
linux·笔记·学习·ubuntu
IMPYLH1 小时前
Linux 的 tail 命令
linux·运维·服务器·bash
生成论实验室1 小时前
《事件关系阴阳博弈动力学:识势应势之道》第五篇:安全关键关系——故障、障碍与冲突
运维·服务器·人工智能·安全·架构
weixin_446260851 小时前
应用实战篇:利用 DeepSeek V4 构建生产级 AI 应用的全流程与最佳实践
大数据·linux·人工智能
zhouwy1131 小时前
C++ STL标准模板库详解
c++