当前位置: 首页 > news >正文

muduo库的思路梳理

前言

对于muduo库源码的剖析我发现还是有些混乱的,所以这里再次梳理一下muduo网络库争取可以简单明了

首先对于muduo库来说,不能想的得太过于复杂,它无非就是一个线程池加上epoll组成的网络库

这里我们从用的角度出发理解muoduo网络库

#include <string>

#include <mymuduo/TcpServer.h>
#include <mymuduo/Logger.h>

class EchoServer
{
public:
    EchoServer(EventLoop *loop, const InetAddress &addr, const std::string &name)
        : server_(loop, addr, name)
        , loop_(loop)
    {
        // 注册回调函数 将用户定义的连接事件处理函数注册进TcpServer中,TcpServer发生连接事件时会执行onConnection函数。
        server_.setConnectionCallback(
            std::bind(&EchoServer::onConnection, this, std::placeholders::_1));

		//将用户定义的可读事件处理函数注册进TcpServer中,TcpServer发生可读事件时会执行onMessage函数。
        server_.setMessageCallback(
            std::bind(&EchoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        // 设置合适的subloop线程数量 你这里设置为3,就和概述篇图中的EventLoop2 EventLoop3 EventLoop4对应,有三个sub EventLoop
        server_.setThreadNum(3);
    }
    void start()
    {
        server_.start();
    }

private:
    // 连接建立或断开的回调函数
    void onConnection(const TcpConnectionPtr &conn)   
    {
        if (conn->connected())
        {
            LOG_INFO("Connection UP : %s", conn->peerAddress().toIpPort().c_str());
        }
        else
        {
            LOG_INFO("Connection DOWN : %s", conn->peerAddress().toIpPort().c_str());
        }
    }

    // 可读写事件回调
    void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp time)
    {
        std::string msg = buf->retrieveAllAsString();
        conn->send(msg);
        conn->shutdown();   // 关闭写端 底层响应EPOLLHUP => 执行closeCallback_
    }

    EventLoop *loop_;
    TcpServer server_;
};

int main() {
    EventLoop loop;
    InetAddress addr(8002, "192.168.194.130"); //InetAddress其实是对socket编程中的sockaddr_in进行封装,使其变为更友好简单的接口而已
    EchoServer server(&loop, addr, "EchoServer");
    server.start(); //启动TcpServer服务器
    loop.loop(); //执行EventLoop::loop()函数,这个函数在概述篇的EventLoop小节有提及
    return 0;
}

直接看main函数,TcpServer对象先创建出来,也就是代码中的server

这里是它的构造函数,也就是说我们构建的TcpServer对象先创建了一个 Acceptor对象和一个线程池对象

对于Acceptor对象来说

创建了一个Channel,同时创建一个非阻塞的套接字fd给这个acceptChannel_

同时也绑定了Acceptor的回调函数,当改fd上有事发生的时候会调用这个handleRead进行相应的处理

这里就结束了,然后是线程池的创建,这个没什么好说的,就是创建线程以及相对于的轮询算法的方法

再看到开始的使用代码,接下来就是Server.start

这里主要是线程池对象开始,这里会选择一个线程,很明显现在只有主线程的epoll运行,所有这里的loop_是mainLoop,接着调用runinLoop,然后会执行Acceptord::lisetn

这个函数就是去监听有没有读事件触发,同时把Channel注册到mainloop的epoll里面去,如果有事件发生那么就会调用Channel注册的回调函数,这个回调函数会调用Acceptor的handleRead

调用这个之后会拿到事件的fd,并且嗲用newConnectionCallback_这个函数,这个函数是它的上层注册的,也就是TcpServer,注册的一个回调函数

 所有进入到newConnection里面去

这个函数的主要目的就是把回调函数注册进Channel,并且把传进来的fd也给Channel,然后通过weakFd通知子loop的线程苏醒,这里线程池会通过轮询的方式把这个连接给到相应的线程里面去,这样也就实现了一个线程一个loop

给到子loop,子loop同样会去事件循环监听事件fd有没有事件发生,从而调用相应的回调函数(这这里的回调是用户给予的)

这里有一张流程图给予理解

相关文章:

  • 前端使用WPS WebOffice 做在线文档预览与编辑
  • Redux,React-redux。基础
  • 【脏读、不可重复读、幻读区别】
  • 云端陷阱:当免费午餐变成付费订阅,智能家居用户如何破局?
  • 【48】指针:函数的“数组入口”与“安全锁”——数组参数传递
  • 【Linux】嵌入式Web服务库:mongoose
  • pytorch与其他ai工具
  • 什么是异步编程,如何在 JavaScript 中实现?
  • 亚马逊多账号风控防护体系构建指南
  • 设计模式类型
  • Android 简化图片加载与显示——使用Coil和Kotlin封装高效工具类
  • 【更新至2023年】各省数字经济相关指标数据集(20个指标)
  • 最长公共子序列问题
  • Spring笔记02-bean的生命周期
  • 传统应用容器化迁移实践
  • 关于matlab和python谁快的问题
  • 【自学笔记】ELK基础知识点总览-持续更新
  • 如何通过数据可视化提升管理效率
  • JAVA-网络编程套接字Socket
  • mysql增、删、改和单表查询多表查询
  • 北斗专访|特赞科技范凌:现在AI主要是“说话”,接下来要“干活”了
  • 地下5300米开辟“人造气路”,我国页岩气井垂深纪录再刷新
  • 习近平举行仪式欢迎巴西总统卢拉访华
  • 排污染黑海水后用沙土覆盖黑泥?汕尾环保部门:非欲盖弥彰
  • 山西省委常委李金科添新职
  • 射箭世界杯上海站摘得两银,中国队新周期冲击韩国缩小差距