本期我们接着来深入项目
相关代码已经上传gitee:仿muduo服务器: 本项目致力于实现一个仿造muduo库的简易并发服务器,为个人项目,参考即可喜欢请点个赞,谢谢
目录
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事件监控
功能 :
-
添加/修改描述符的事件监控(不存在则添加,存在则修改)
-
移除描述符的事件监控
封装思想 :
-
必须拥有一个epoll的操作句柄
-
拥有一个struct epoll_event结构数组,监控时保存所有的活跃事件
-
使用hash表管理描述符与描述符对应的事件管理Channel对象
逻辑流程 :
-
对描述符进行监控,通过Channel才能知道描述符需要监控什么事件
-
当描述符就绪了,通过描述符在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_();
}
}
本期内容到这里就结束了,喜欢请关注以下,谢谢
封面图自取:
