仿Muduo库实现高并发服务器——Channel模块

Channel模块简单介绍: 

        Channel模块在本项目中的使用。

        下面这张图 是channel模块,poller模块,TimerWheel模块,EventLoop模块,LoopThreadPool模块进行组合。便于大家对这个项目的理解,因为代码看起来挺复杂的。

        Channel是进行事件处理的,将要监控什么事件通过_events成员变量进行设置,再通过_revents成员变量对就绪的事件进行处理,调用对应的回调函数。

        为什么要这样处理呢?

        原因是Poller模块,它是用epoll I/O复用进行实现的,具体细节在这篇文章中有讲

        uint32_t _events;  // 当前需要监控的事件
        uint32_t _revents; // 当前连接触发的事件

         _events和_revents中的每一个比特位都表示一个事件。

        取消事件和设置事件都是对_events进行位操作。

启动要监控的事件并添加到Poller模块中: 

        //当前是否监控了可读
        bool ReadAble() { return (_events & EPOLLIN); } 
        //当前是否监控了可写
        bool WriteAble() { return (_events & EPOLLOUT); }
        //启动读事件监控
        void EnableRead() { _events |= EPOLLIN; Update(); }
        //启动写事件监控
        void EnableWrite() { _events |= EPOLLOUT; Update(); }
        //关闭读事件监控
        void DisableRead() { _events &= ~EPOLLIN; Update(); }
        //关闭写事件监控
        void DisableWrite() { _events &= ~EPOLLOUT; Update(); }
        //关闭所有事件监控
        void DisableAll() { _events = 0; Update(); }

      下图就是Update()函数调用的实现,有人就可能说 这函数名都不一样,你是不是接错了,当然不是,因为我就在这里卡了半天。

        这个函数是在EventLoop模块中调用的 事件管理模块。重点就在管理上,对于添加/删除channel对象都是人家EventLoop模块说了算。

        注意:关于事件启动,他不是简简单单的就进行事件的设置,他后面还有个Update()函数调用,它的作用是什么?将新的Channel对象添加到Poller模块中,进行管理。

        我为什么要说这个?原因是TimerWheel定时器模块和EventLoop模块中分别会创建一个文件描述符,创建文件描述符无非就是想要知道这个文件描述符上面发生了什么事件。那么就要在创建一个Channel对象将这个文件描述符管理起来。

        创建Channel对象之后,还必须要将这个Channel对象添加到Poller模块中进行管理,否则就不会将就绪事件写到_revents中去。这就是为什么我要介绍Update()。

        如果你还是不懂,就接着去看代码,你看TimerWheel定时器模块和EventLoop模块中创建的文件描述符将怎样进行事件监控,不清楚就看看上面的解释。

        为什么要这么干?原因是:Poller模块的作用,他是监控事件的,你对channel对象设置的监控事件,Poller模块会将就绪的事件保存到_revents中,这就是为什么要在Poller管理中进行管理。

就绪事件如何处理:

        _revents中存储的是已经就绪的事件,对于已经就绪的事件就调用对应的回调函数进行处理。

        //事件处理,一旦连接触发了事件,就调用这个函数,自己触发了什么事件如何处理自己决定
        void HandleEvent() {
            if ((_revents & EPOLLIN) || (_revents & EPOLLRDHUP) || (_revents & EPOLLPRI)) {
                /*不管任何事件,都调用的回调函数*/
                if (_read_callback) _read_callback();
            }
            /*有可能会释放连接的操作事件,一次只处理一个*/
            if (_revents & EPOLLOUT) {
                if (_write_callback) _write_callback();
            }else if (_revents & EPOLLERR) {
                if (_error_callback) _error_callback();//一旦出错,就会释放连接,因此要放到前边调用任意回调
            }else if (_revents & EPOLLHUP) {
                if (_close_callback) _close_callback();
            }
            if (_event_callback) _event_callback();
        }

Channel模块回调函数: 

        这里的回调函数需要外部进行设置,Channel模块不知道如何去处理就绪事件。

回调函数是在Connection模块中默认构造函数中进行设置。 

        void SetREvents(uint32_t events) { _revents = events; }//设置实际就绪的事件
        void SetReadCallback(const EventCallback &cb) { _read_callback = cb; }
        void SetWriteCallback(const EventCallback &cb) { _write_callback = cb; }
        void SetErrorCallback(const EventCallback &cb) { _error_callback = cb; }
        void SetCloseCallback(const EventCallback &cb) { _close_callback = cb; }
        void SetEventCallback(const EventCallback &cb) { _event_callback = cb; }

Channel模块中为什么会有EventLoop对象: 

void Channel::Remove() { return _loop->RemoveEvent(this); }
void Channel::Update() { return _loop->UpdateEvent(this); }

        上面这个代码,就是对Channel对象进行 添加/删除 用的,不然Channel模块中凭什么会有EventLoop对象,都是有原因的,EventLoop对象 他是进行事件管理的。降低耦合,这个我不能为你们讲,因为我对于代码设计还是不太好的。

        Channel对象就是一个打工仔。给你什么调用函数,雇主说在什么情况下执行,就在什么情况下执行,每个打工仔干的活,还有可能不一样。

        这个模块就是 记录文件描述符 和 想要监控的事件,实际就绪的事件,以及事件发生之后要怎么做,判断有没有监控这个事件,获取事件。

下面就是channel模块总体代码:

class Poller;
class EventLoop;
class Channel {
    private:
        int _fd;
        EventLoop *_loop;
        uint32_t _events;  // 当前需要监控的事件
        uint32_t _revents; // 当前连接触发的事件
        using EventCallback = std::function<void()>;
        EventCallback _read_callback;   //可读事件被触发的回调函数
        EventCallback _write_callback;  //可写事件被触发的回调函数
        EventCallback _error_callback;  //错误事件被触发的回调函数
        EventCallback _close_callback;  //连接断开事件被触发的回调函数
        EventCallback _event_callback;  //任意事件被触发的回调函数
    public:
        Channel(EventLoop *loop, int fd):_fd(fd), _events(0), _revents(0), _loop(loop) {}
        int Fd() { return _fd; }
        uint32_t Events() { return _events; }//获取想要监控的事件
        void SetREvents(uint32_t events) { _revents = events; }//设置实际就绪的事件
        void SetReadCallback(const EventCallback &cb) { _read_callback = cb; }
        void SetWriteCallback(const EventCallback &cb) { _write_callback = cb; }
        void SetErrorCallback(const EventCallback &cb) { _error_callback = cb; }
        void SetCloseCallback(const EventCallback &cb) { _close_callback = cb; }
        void SetEventCallback(const EventCallback &cb) { _event_callback = cb; }
        //当前是否监控了可读
        bool ReadAble() { return (_events & EPOLLIN); } 
        //当前是否监控了可写
        bool WriteAble() { return (_events & EPOLLOUT); }

        //启动/关闭是改变了监控事件 但你需要告诉人家监控模块(poller)
        //启动读事件监控
        void EnableRead() { _events |= EPOLLIN; Update(); }
        //启动写事件监控
        void EnableWrite() { _events |= EPOLLOUT; Update(); }
        //关闭读事件监控
        void DisableRead() { _events &= ~EPOLLIN; Update(); }
        //关闭写事件监控
        void DisableWrite() { _events &= ~EPOLLOUT; Update(); }
        //关闭所有事件监控
        void DisableAll() { _events = 0; Update(); }
        //移除监控
        void Remove();
        void Update();
        //事件处理,一旦连接触发了事件,就调用这个函数,自己触发了什么事件如何处理自己决定
        void HandleEvent() {
            if ((_revents & EPOLLIN) || (_revents & EPOLLRDHUP) || (_revents & EPOLLPRI)) {
                /*不管任何事件,都调用的回调函数*/
                if (_read_callback) _read_callback();
            }
            /*有可能会释放连接的操作事件,一次只处理一个*/
            if (_revents & EPOLLOUT) {
                if (_write_callback) _write_callback();
            }else if (_revents & EPOLLERR) {
                if (_error_callback) _error_callback();//一旦出错,就会释放连接,因此要放到前边调用任意回调
            }else if (_revents & EPOLLHUP) {
                if (_close_callback) _close_callback();
            }
            if (_event_callback) _event_callback();
        }
};


void Channel::Remove() { return _loop->RemoveEvent(this); }
void Channel::Update() { return _loop->UpdateEvent(this); }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

挣扎的泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值