目录
本项目由 岩禾溪 原创
项目实战+新特性用法介绍
开源代码+博客解析+视频讲解GitHub+CSDN+BiliBili同步更新,三个平台同名【岩禾溪】
GitHub代码链接: GitHub - YanHeXi/muduo_cpp20
项目讲解视频连接:岩禾溪的个人空间-岩禾溪个人主页-哔哩哔哩视频
你的关注是我更新的最大动力
InetAddress.ixx
export module InetAddress;
export import <string>;
export import <arpa/inet.h>;
import <netinet/in.h>;
export class InetAddress
{
public:
explicit InetAddress(uint16_t port = 0,
std::string_view ip = "127.0.0.1");
explicit InetAddress(const sockaddr_in &addr)
: addr_(addr) {}
std::string toIp() const;
std::string toIpPort() const;
uint16_t toPort() const { return ntohs(addr_.sin_port); }
const sockaddr_in *getSockAddr() const { return &addr_; }
void setSockAddr(const sockaddr_in &addr) { addr_ = addr; }
private:
sockaddr_in addr_;
};
模块介绍
该模块名为 InetAddress
,用于处理网络地址的表示和操作。它依赖于 C++20 中的模块特性进行导出和导入相关头文件,并提供网络地址的基本功能。
类 InetAddress
:
-
构造函数:
explicit InetAddress(uint16_t port = 0, std::string_view ip = "127.0.0.1");
:这是一个构造函数,用于创建一个 InetAddress 类的实例。它接受一个端口号和一个 IP 地址字符串,默认情况下端口号为0,IP地址为 "127.0.0.1"。
-
成员函数:
std::string toIp() const;
:返回当前InetAddress
实例的 IP 地址字符串表示。std::string toIpPort() const;
:返回当前InetAddress
实例的 IP 地址和端口号的组合字符串表示。uint16_t toPort() const;
:返回当前InetAddress
实例的端口号。const sockaddr_in *getSockAddr() const;
:返回指向当前InetAddress
实例中 sockaddr_in 结构的指针。void setSockAddr(const sockaddr_in &addr);
:设置当前InetAddress
实例中的 sockaddr_in 结构。
C++20 新特性内容:
- Modules (模块):
export module InetAddress;
和export/import
关键字用于导出和导入模块,将InetAddress
类的定义和相关的头文件进行模块化管理。 - std::string_view:
std::string_view
是 C++17 引入的,但在 C++20 中得到了增强。它用于在不拷贝字符串的情况下表示和操作字符串。 - Uniform Initialization (统一初始化):在构造函数中,使用了统一初始化方式,允许在一个构造函数中同时提供多个默认参数值。
InetAddress.cpp
import InetAddress;
import <cstring>;
import <netinet/in.h>;
InetAddress::InetAddress(uint16_t port, std::string_view ip)
{
addr_.sin_family = AF_INET;
addr_.sin_port = htons(port);
inet_pton(AF_INET, ip.data(), &addr_.sin_addr);
}
std::string InetAddress::toIp() const
{
char buf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &addr_.sin_addr, buf, INET_ADDRSTRLEN);
return std::string(buf);
}
std::string InetAddress::toIpPort() const
{
char buf[INET_ADDRSTRLEN + 8];
sprintf(buf, "%s:%u", toIp().c_str(), ntohs(addr_.sin_port));
return std::string(buf);
}
函数实现解释:
-
构造函数
InetAddress::InetAddress(uint16_t port, std::string_view ip)
:- 设置了
sockaddr_in
结构体的sin_family
成员为AF_INET
,表示使用 IPv4 地址族。 - 使用
htons()
函数将传入的port
(端口号)转换为网络字节序(Big-endian)。 - 使用
inet_pton()
函数将传入的ip
(IPv4 地址字符串)转换为网络字节序的二进制格式,存储到sockaddr_in
结构体的sin_addr
成员中。
- 设置了
-
成员函数
std::string InetAddress::toIp() const
:- 使用
inet_ntop()
函数将存储在sockaddr_in
结构体的sin_addr
中的网络字节序的 IPv4 地址转换为可读的点分十进制字符串格式,并返回该字符串。
- 使用
-
成员函数
std::string InetAddress::toIpPort() const
:- 调用
toIp()
函数获取点分十进制表示的 IP 地址字符串。 - 使用
ntohs()
函数将sockaddr_in
结构体中存储的网络字节序的端口号转换为主机字节序(Little-endian)的端口号。 - 使用
sprintf()
函数将 IP 地址字符串和端口号格式化为"ip:port"
的形式,并返回该字符串。
- 调用
Channel.ixx
export module Channel;
export import EventLoop;
import nocopyable;
export import Timestamp;
import <functional>;
export import <memory>;
import <poll.h>;
export class Channel : nocopyable
{
public:
using EventCallback = std::function<void()>;
using ReadEventCallback = std::function<void(Timestamp)>;
Channel(EventLoop *loop, int fd);
~Channel();
void handleEvent(Timestamp receiveTime);
void setReadCallback(ReadEventCallback cb) { readCallback_ = std::move(cb); }
void setReadCallback(EventCallback cb) { writeCallback_ = std::move(cb); }
void setCloseCallback(EventCallback cb) { closeCallback_ = std::move(cb); }
void setErrorCallback(EventCallback cb) { errorCallback_ = std::move(cb); }
void tie(const std::shared_ptr<void> &);
int fd() const { return fd_; }
int events() const { return events_; }
void set_revents(int revt) { revents_ = revt; }
bool isNoneEvent() const { return events_ == kNoneEvent; }
bool isWriting() const { return events_ & kWriteEvent; }
bool isReading() const { return events_ & kReadEvent; }
void enableReading()
{
events_ |= kReadEvent;
update();
}
void disableReading()
{
events_ &= ~kReadEvent;
update();
}
void enableWriting()
{
events_ |= kWriteEvent;
update();
}
void disableWriting()
{
events_ &= ~kWriteEvent;
update();
}
void disableAll()
{
events_ = kNoneEvent;
update();
}
int index() { return index_; }
void set_index(int idx) { index_ = idx; }
EventLoop *ownerLoop() { return loop_; }
void remove();
private:
void handleEventWithGuard(Timestamp receiveTime);
void update();
static const int kNoneEvent;
static const int kReadEvent;
static const int kWriteEvent;
EventLoop *loop_;
const int fd_;
int events_;
int revents_;
int index_;
std::weak_ptr<void> tie_;
bool tied_;
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
};
模块介绍
该模块名为 Channel
,实现了一个用于管理文件描述符事件的类。它依赖于 EventLoop
类和其他一些头文件和类型,使用了 nocopyable
类作为基类。
类 Channel
:
-
构造函数:
Channel(EventLoop *loop, int fd);
:接受一个指向EventLoop
对象的指针和一个文件描述符作为参数,用于创建Channel
类的实例。
-
析构函数:
~Channel();
:析构函数用于释放资源,清理对象。
-
成员函数:
void handleEvent(Timestamp receiveTime);
:处理事件的方法,接收一个Timestamp
参数。- 四个
set
函数用于设置不同类型事件的回调函数。 tie(const std::shared_ptr<void> &);
:将Channel
与给定的shared_ptr
关联起来。- 多个操作函数用于控制事件的状态,如
enableReading()
、disableReading()
等。 remove()
:从EventLoop
中移除当前Channel
。
-
私有函数:
update()
:更新事件。handleEventWithGuard(Timestamp receiveTime);
:带有保护的事件处理函数。
Channel.cpp
import EventLoop;
import <sys/epoll.h>;
import Logger;
import Channel;
const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = EPOLLIN | EPOLLPRI;
const int Channel::kWriteEvent = EPOLLOUT;
Channel::Channel(EventLoop *loop, int fd)
: loop_(loop),
fd_(fd),
events_(0),
revents_(0),
index_(-1),
tied_(false)
{
}
Channel::~Channel() {}
void Channel::tie(const std::shared_ptr<void> &obj)
{
tie_ = obj;
tied_ = true;
}
void Channel::update()
{
// loop_->updateChannel(this);
}
void Channel::remove()
{
// loop_->removeChannel(this);
}
void Channel::handleEvent(Timestamp receiveTime)
{
if (tied_)
{
std::shared_ptr<void> guard = tie_.lock();
if (guard)
{
handleEventWithGuard(receiveTime);
}
}
else
{
handleEventWithGuard(receiveTime);
}
}
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
Logger &logger = Logger::instance();
logger.log(Logger::LogLevel::DEBUG,
"This is an informational message. Revents value: {}",
std::to_string(revents_));
if ((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN))
{
if (closeCallback_)
{
closeCallback_();
}
}
if (revents_ & EPOLLERR)
{
if (errorCallback_)
{
errorCallback_();
}
}
if (revents_ & (EPOLLIN | EPOLLPRI))
{
if (readCallback_)
{
readCallback_(receiveTime);
}
}
if (revents_ & EPOLLOUT)
{
if (writeCallback_)
{
writeCallback_();
}
}
}
模块导入和常量定义:
- 使用了
EventLoop
、sys/epoll.h
、Logger
和Channel
模块。 - 定义了三个常量
Channel::kNoneEvent
、Channel::kReadEvent
和Channel::kWriteEvent
,分别表示无事件、读事件和写事件,对应EPOLLIN
、EPOLLPRI
和EPOLLOUT
。
类 Channel
的函数实现:
-
构造函数
Channel::Channel(EventLoop *loop, int fd)
:- 接受一个指向
EventLoop
对象的指针和一个文件描述符作为参数。 - 初始化了
Channel
类的成员变量,如events_
、revents_
等。
- 接受一个指向
-
成员函数
void tie(const std::shared_ptr<void> &obj)
:- 将
Channel
与给定的shared_ptr
关联起来,标记为已关联状态。
- 将
-
成员函数
void update()
和void remove()
:- 这两个函数被注释掉了,通常用于在
EventLoop
中更新和移除当前的Channel
。
- 这两个函数被注释掉了,通常用于在
-
成员函数
void handleEvent(Timestamp receiveTime)
和void handleEventWithGuard(Timestamp receiveTime)
:handleEvent()
是对事件处理函数的调度器,检查是否与shared_ptr
相关联,并调用handleEventWithGuard()
。handleEventWithGuard()
是对具体事件的处理函数,根据触发的事件类型执行相应的回调函数,如读、写、错误或关闭。
关于注释部分:
- 代码中有一些注释掉的代码段,这里我还没调好,之后会更新,目前编译可以通过
- 这里包括了日志记录和
Logger
类的使用。这些部分将被用于调试或记录日志,在生产环境中会被用于追踪事件处理过程中的状态和信息。
总结
可以无所谓,不能无所获
——岩