首页 > 编程语言 >[岩禾溪] C++20项目 muduo网络库 项目实战 (2)InetAddress & Channel

[岩禾溪] C++20项目 muduo网络库 项目实战 (2)InetAddress & Channel

时间:2023-11-25 10:23:21浏览次数:34  
标签:std muduo const 岩禾 import void InetAddress 20 Channel

 目录

 ​本项目由 岩禾溪 原创

InetAddress.ixx

模块介绍

类 InetAddress:

C++20 新特性内容:

InetAddress.cpp

函数实现解释:

Channel.ixx

模块介绍

类 Channel:

Channel.cpp

模块导入和常量定义:

类 Channel 的函数实现:

关于注释部分:

更新Logger

Logger.ixx(更新)

 ​编辑本项目由 岩禾溪 原创

 

项目实战+新特性用法介绍
开源代码+博客解析+视频讲解

 

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 新特性内容:

  1. Modules (模块)export module InetAddress;export/import 关键字用于导出和导入模块,将 InetAddress 类的定义和相关的头文件进行模块化管理。
  2. std::string_viewstd::string_view 是 C++17 引入的,但在 C++20 中得到了增强。它用于在不拷贝字符串的情况下表示和操作字符串。
  3. 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_();
        }
    }
}

模块导入和常量定义:

  • 使用了 EventLoopsys/epoll.hLoggerChannel 模块。
  • 定义了三个常量 Channel::kNoneEventChannel::kReadEventChannel::kWriteEvent,分别表示无事件、读事件和写事件,对应 EPOLLINEPOLLPRIEPOLLOUT

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 类的使用。这些部分将被用于调试或记录日志,在生产环境中会被用于追踪事件处理过程中的状态和信息。

更新Logger

删除了Logger.cpp,简化了Logger模块的内容,使用更加直观

Logger.ixx(更新)

export module Logger;
export import <string>;
import <sstream>;
import <iostream>;
export class Logger
{
public:
    enum class LogLevel
    {
        INFO,
        ERROR,
        FATAL,
        DEBUG,
    };

    static Logger &instance()
    {
        static Logger logger;
        return logger;
    }

    void setLogLevel(LogLevel level)
    {
        logLevel_ = level;
    }

    template <typename... Args>
        requires(std::convertible_to<Args, std::string> && ...)
    void log(LogLevel level, const std::string &format, Args &&...args)
    {
        std::ostringstream oss;
        oss << "[" << static_cast<int>(level) << "] : ";
        ((oss << format << std::forward<Args>(args)), ...);
        std::string message = oss.str();

        std::cout << message << std::endl;
    }

private:
    LogLevel logLevel_ = LogLevel::INFO;
};

 总结

可以无所谓,不能无所获

                                        ——岩

标签:std,muduo,const,岩禾,import,void,InetAddress,20,Channel
From: https://www.cnblogs.com/YanHeXi/p/17855241.html

相关文章

  • 2023版 STM32实战7 通用同步/异步收发器(串口)F103/F407
    串口简介和习惯-1-通用同步异步收发器(USART)能够灵活地与外部设备进行全双工数据交换,满足外部设备对工业标准NRZ异步串行数据格式的要求。-2-硬件流控制一般是关闭的-3-波特率指单位时间传输bit个数-4-数据位一般是8位-5-一般无校验位编写代码思路-1-参考帮助手册(F1/F4都有)-2......
  • 2023.11.25学习笔记
    集合SubsetSumsP1466[USACO2.2]集合SubsetSums-洛谷|计算机科学教育新生态(luogu.com.cn)背包板子题,有一说一看出来很简单贴accode#include<iostream>usingnamespacestd;longlonga[50];intmain(){intn;cin>>n;intsum=0,ans=0;fo......
  • NOIP2023游记
    之前忘写了现在忘了(雾)Day0在七高考。欣赏了一会走廊边上的展示柜。进考场。印象里是圆桌,结果还是常规的几排。(好像各个考场不一样……?)开考之后因为键盘的奇怪手感而奇怪了很久。一行头文件打了半天,,。P.S.之前同学说过七高键盘难用,然而没当回事TAT。T1有点水……但是还是......
  • 2023版 STM32实战7 通用同步/异步收发器(串口)F103/F407
    串口简介和习惯-1-通用同步异步收发器(USART)能够灵活地与外部设备进行全双工数据交换,满足外部设备对工业标准NRZ异步串行数据格式的要求。 -2-硬件流控制一般是关闭的 -3-波特率指单位时间传输bit个数 -4-数据位一般是8位 -5-一般无校验位 编写代码思路 -......
  • 20231124
    又是容斥的一天呢。容斥做傻了,每次推容斥系数都能推错。放学的时候@Super_Cube给我说了明天gm打算从「CDQ解决二维偏序问题」讲起,瞬间就不想去听gm上课了。但是@Super_Cube给我说「偶尔听gm发癫还是能放松一下的。」我说『不行,浪费我时间。』「也就两个小时而已,况且“......
  • 20211128《信息安全系统设计与实现》第十三章学习笔记
    一、任务内容自学教材第13章,提交学习笔记(10分)1.知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容(4分)“我在学***X知识点,请你以苏格拉底的方式对我进行提问,一次一个问题”核心是要求GPT:“请你以苏格......
  • 2023.11.24 日记 夜浓浓
    轻闲的一天。夜浓浓地笼罩在窗外,远远地依稀见到明暗的城市灯火。白日久违地听孙佳讲课,内容是没细听了,只是边学着英语的《语法通霸》边挂着一只耳朵听讲(纪中的英语老师笑着对我们仨说,挂着一只耳朵听课。她没有解释下去,我约摸是边做自己的事边听课,偶尔会被课堂吸引。不知这样是否是......
  • NOIP2023 退役记
    省流:爆单了。\(\rmDay\0\)中午感觉身体发冷,有一种不详的预感。下午润去看病,好像寄了。做了甲流的检测,不过好像要考\(\rmNOIP\)时才能出结果。吃了退烧药,但还是\(\rm38\)度多。没有胃口吃晚饭。晚上到了杭州稍微好了一点,喝了一点粥。\(\rmDay\1\)早上一测温度还......
  • CSP2023+NOIP2023邮寄
    本文同时发表在个人洛谷博客。CSPDay-1上午打德文布置的毒瘤信心赛,据说请了一个D类金验题,没有成功ak。打完没信心了。下午去下沙。有点像小县城。晚饭在下沙天街,好评。颓废。Day0上午打J。开场3分钟没过T1,然后发现次数是\(\log\)级别的,无脑暴力。菜死了。菜死了。菜......
  • NOIP 2023 游记
    省流:某个网站测的100+20+5+56=181OI到底是一门什么样的学科?所谓的思维题到底是什么?这些已经随着我的OI生涯结束而消逝了。OI似乎拼的是积累+运气,就像2022一直考tarjan一样。我不相信运气,但是这个真的是没有办法避免的,回想起noip2022,我冲T3两个小时电脑......