首页 > 编程语言 >muduo源码剖析之前序

muduo源码剖析之前序

时间:2023-11-25 12:24:45浏览次数:28  
标签:std muduo 阻塞 剖析 源码 IO EchoServer net

目录

生疏

  1. 在进行日志库的开发时,涉及到的几个重要的宏:
    1. __FILE__:文件名称,包含路径
    2. __LINE__:行号
    3. __func__:函数名称
  2. cpp中含有默认参数时,函数声明和定义不能同时出现
  3. cpp中静态成员变量以及静态成员函数在定义时候不能加static关键字,同理还有virtual关键字
  4. __thread关键字:该关键字修饰的变量为线程局部存储变量
  5. FD_CLOEXEC标志:这是文件描述符的标志之一。在新创建一个文件描述符时都应该设置这个标志位,特别是多线程程序下。man文档描述如下:

  6. __VA_ARGS__:一个可变参数宏,一般使用在宏定义中,例如:
#define LOG_ERROR(formatString, ...)\
do {\
    Logger& logger = Logger::getInstance();\
    logger.setLogLevel(static_cast<int>(Logger::LoggerLevel::ERROR));\
    char buffer[1024] = {0};\
    snprintf(buffer, 1024, formatString, ##__VA_ARGS__);\
} while (0)
  1. 名称空间
    1. 匿名命名空间:在使用namespace关键字定义名称空间时不指定名称,例如namespace {}。
    2. 引用全局命名空间时一般指定::,例如main函数就是在全局命名空间下。
  2. #pragma GCC diagnostic ignored的使用
  3. extern关键字的作用
  4. __builtin_expect的使用
  5. syscall(SYS_gettid)函数
  6. open("/dev/null)
  7. enable_shared_from_this的使用
  8. mutable关键字的使用:
    1. 修饰类的成员变量
    mutable std::mutex mutex_;
    
    size_t EventLoop::queueSize() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return pendingFunctors_.size();
    }
    
    
    error: binding reference of type ‘std::lock_guard<std::mutex>::mutex_type&’ {aka ‘std::mutex&’} to ‘const std::mutex’ discards qualifiers
    
    1. lambda表达式中修饰以值捕获的变量,这样仅仅可以让编译通过。但是lambda表达式的函数体中修改以值捕获的变量不会影响外部变量的改变。自导自演
  9. SO_KEEPALIVE选项:这是一个套接字选项,用于判断连接是否存活。但是在实际的游戏服务器开发中,在应用层上实现心跳包机制。

阻塞、非阻塞

  1. 阻塞:典型的一次IO一般分为两个阶段:数据准备以及数据读写(数据在内核空间与用户空间之间的往返)。在数据准备阶段,调用IO方法的线程进入阻塞状态。例如recv方法默认就是阻塞的,如果没有数据到来就会阻塞当前线程,当前线程将会挂起,状态从运行态变为阻塞态
  2. 非阻塞:典型的一次IO一般分为两个阶段:数据准备以及数据读写。在数据准备阶段,即使没有数据,调用IO方法的线程也不会被阻塞,而是直接返回值。例如:当套接字设置为非阻塞的,调用recv方法将会立即返回,即使内核的接收缓冲区没有数据。这样的话线程还能有机会继续运行着。
  3. 综上:根据系统io操作的就绪状态来判断一个方法是阻塞的还是非阻塞的。

同步、异步

  1. 同步:典型的一次IO一般分为两个阶段:数据准备以及数据读写。在数据读写阶段,例如recv接口就是一个同步IO接口,如果TCP内核的接收缓冲区有数据,用户态的应用程序则会等将内核的接收缓冲区数据拷贝到用户缓冲区以后才会继续执行下去。一些IO复用函数例如poll、epoll、select都是同步IO
  2. 异步:典型的一次IO一般分为两个阶段:数据准备以及数据读写。在数据读写阶段,用户态的应用程序需要给内核传递一个buffer(这是一个传入传出参数)以及指定一种通知方式,当内核准备好数据就会以信号、事件回调等方式通知应用程序。这一期间,用户态的应用程序可以继续执行下去,不用等内核准备数据。例如aio_read接口以及aio_write接口。
  3. 综上:根据应用程序和内核的交互方式来判断异步和同步,在数据读写阶段,应用程序是否需要等待数据的准备才可以继续执行其他逻辑。

Linux上的五种IO模型

  1. 阻塞IO:分为同步阻塞IO和异步阻塞IO。同步阻塞IO常用,异步阻塞IO一般少见。
  2. 非阻塞IO:分为同步非阻塞IO和异步非阻塞IO。对于同步非阻塞IO,因为非阻塞IO会立即返回,所以一般需要在循环中去判断数据是否准备好,但这个循环判断也是在白白消耗CPU,没有意义。因此只有在事件已经发生的情况下操作非阻塞IO才能提高程序的效率,因此开发中常使用IO多路复用或者信号驱动。
  3. IO多路复用:IO复用函数本身默认是阻塞的,但是可以通过超时参数设置成非阻塞的。
  4. 信号驱动:应用程序调用系统调用来注册信号的处理程序,当发生相应的事件以后,内核会通知应用程序,在这期间应用程序可以继续执行其他逻辑。
  5. 异步IO:数据从内核空间拷贝到用户空间或者从用户空间拷贝到内核空间,也由操作系统完成,而不是由用户程序完成。用户程序可以放飞自我,继续执行其他的逻辑。数据由操作系统拷贝完成以后,应用程序处理数据。

Reactor模式

  1. 主流的网络库比如说libevent,muduo都是用的这个模式,这是一种高效的事件处理模式,同步IO模型通常使用Reactor模式实现。
  2. 在Reactor模式中,主线程只负责监听文件描述符上是否有事件发生,有的话立即将事件通知工作线程。读写数据、接受新的连接、以及处理客户请求都在工作线程中完成。Reactor模式的重要组件如下:
    1. Event:事件
    2. Reactor:反应堆
    3. Demultiplex:事件分发器
    4. EventHandler:事件处理器

muduo的基本使用

  1. 参考https://github.com/chenshuo/muduo-tutorial/tree/master,将其克隆至本地,然后选择第二种构建方式。
  2. echo-server示例如下:
    1. EchoServer.h
    #pragma once
    
    #include "muduo/net/TcpServer.h"
    #include "muduo/net/EventLoop.h"
    #include "muduo/net/InetAddress.h"
    #include "muduo/net/TcpConnection.h"
    #include "muduo/base/AsyncLogging.h"
    #include "muduo/base/Logging.h"
    #include "muduo/base/CurrentThread.h"
    
    #include <memory>
    
    class EchoServer {
    public:
        EchoServer(muduo::net::EventLoop* loop, const muduo::net::InetAddress addr);
    
        void start() {
            _serverptr->start();
        }
    private:
        std::unique_ptr<muduo::net::TcpServer> _serverptr;
        muduo::net::EventLoop * _eventloop;
    
        void onConnection(const muduo::net::TcpConnectionPtr&);
        void onMessage(const muduo::net::TcpConnectionPtr&,
                                muduo::net::Buffer*,
                                muduo::Timestamp);
    };
    
    1. EchoServer.cc
    #include "EchoServer.h"
    
    
    #include <functional>
    #include <string>
    
    EchoServer::EchoServer(muduo::net::EventLoop* loop, const muduo::net::InetAddress addr)
        : _eventloop(loop){
        _serverptr.reset(new muduo::net::TcpServer(_eventloop, addr, "ECHO-SERVER"));
        _serverptr->setConnectionCallback(std::bind(&EchoServer::onConnection, this, std::placeholders::_1));
        _serverptr->setMessageCallback(std::bind(&EchoServer::onMessage,
            this,
            std::placeholders::_1,
            std::placeholders::_2,
            std::placeholders::_3));
        _serverptr->setThreadNum(4);
    }
    
    void EchoServer::onConnection(const muduo::net::TcpConnectionPtr& conn) {
        LOG_TRACE << conn->peerAddress().toIpPort() << " -> "
                  << conn->localAddress().toIpPort() << " is "
                  << (conn->connected() ? "UP" : "DOWN");
    }
    
    void EchoServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
                            muduo::net::Buffer* buf,
                            muduo::Timestamp time) {
        std::string msg(buf->retrieveAllAsString());
        LOG_TRACE << conn->name() << " recv " << msg.size() << " bytes at " << time.toString();
        conn->send(msg);
    
    }
    
    
    1. main.cc
    #include "EchoServer.h"
    
    int kRollSize = 500*1000*1000;
    
    std::unique_ptr<muduo::AsyncLogging> g_asyncLog;
    
    void asyncOutput(const char* msg, int len) {
        g_asyncLog->append(msg, len);
    }
    
    void setLogging(const char* argv0) {
        muduo::Logger::setOutput(asyncOutput);
        char name[256];
        strncpy(name, argv0, 256);
        g_asyncLog.reset(new muduo::AsyncLogging(::basename(name), kRollSize));
        g_asyncLog->start();
    }
    
    
    int main(int argc, const char* argv[]) {
        // 设置日志级别
        muduo::Logger::setLogLevel(muduo::Logger::TRACE);
        
        setLogging(argv[0]);
      
        LOG_INFO << "pid = " << getpid() << ", tid = " << muduo::CurrentThread::tid();
        muduo::net::EventLoop loop;
        muduo::net::InetAddress listenAddr(8888);
        EchoServer server(&loop, listenAddr);
      
        server.start();
      
        loop.loop();
    
        return 0;
    }
    
    1. 编译:g++ main.cc EchoServer.cc -o test -lmuduo_base -lmuduo_net
  3. 从echo server这个例子,可知基于muduo开发服务器程序的步骤如下:
    1. 你的服务器主类中组合TcpServer对象以及EventLoop对象(这个EventLoop其实就是一个mainLoop)
    2. 在服务器主类中注册处理用户连接的创建和断开的回调函数以及处理读写事件的回调函数
    3. 设置合适的服务器线程数量,内部的EventThreadPool会根据设置的线程数量创建相应个数的subLoop。如果不设置线程数量,则服务器程序默认只有1个mainLoop负责监听客户端的连接请求以及和客户端通信。
    4. 启动server
    server.start();
    
    1. mainLoop进入循环,监听客户端的连接请求
    loop.loop();
    

标签:std,muduo,阻塞,剖析,源码,IO,EchoServer,net
From: https://www.cnblogs.com/xiaocer/p/17855376.html

相关文章

  • [岩禾溪] C++20项目 muduo网络库 项目实战 (1)Logger & Timestamp
    ​  ​编辑本项目由岩禾溪原创 项目实战+新特性用法介绍开源代码+博客解析+视频讲解 GitHub+CSDN+BiliBili同步更新,三个平台同名【岩禾溪】视频讲解和代码链接在文章末尾,你的关注是我更新的最大动力项目环境本项目采用C++20开发精简Muduo网络库BuildTool:Xma......
  • [岩禾溪] C++20项目 muduo网络库 项目实战 (2)InetAddress & Channel
    ​ 目录 ​本项目由岩禾溪原创InetAddress.ixx模块介绍类InetAddress:C++20新特性内容:InetAddress.cpp函数实现解释:Channel.ixx模块介绍类Channel:Channel.cpp模块导入和常量定义:类Channel的函数实现:关于注释部分:更新LoggerLogger.ixx(更新) ​编辑本......
  • 开源小说站源码php+付费阅读小说网站系统源码附全自动采集和搭建教程
    随着互联网的普及,网络小说已成为现代人生活不可或缺的一部分。与此同时,越来越多的人开始关注小说网站源码的开发,希望通过开发自己的小说网站来满足读者的需求。本文将从小说网站源码的设计、开发、运维等方面进行探讨,以帮助读者更好地了解小说网站源码的相关知识。源码:xsymz.icu......
  • Java二级医院区域HIS信息管理系统源码(SaaS服务)
    一个好的HIS系统,要具有开放性,便于扩展升级,增加新的功能模块,支撑好医院的业务的拓展,而且可以反过来给医院赋能,最终向更多的患者提供更好的服务。系统采用前后端分离架构,前端由Angular、JavaScript开发;后端使用Java语言开发。融合B/S版电子病历系统,支持电子病历四级。系统运行稳定、......
  • 基于django的4s店客户管理系统-计算机毕业设计源码+LW文档
    摘 要 进入21世纪网络和计算机得到了飞速发展,并和生活进行了紧密的结合。目前,网络的运行速度以达到了千兆,覆盖范围更是深入到生活中的角角落落。这就促使管理系统的发展。网上办公可以实现远程处理事务,远程提交工作和随时追踪工作的状态。网上管理系统给人们带来前所未有的体......
  • 基于python的计算机网络在线考试系统-计算机毕业设计源码+LW文档
    摘 要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本基于python的计算机网络在线考试系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员......
  • FreeRTOS深入教程(信号量源码分析)
    (文章目录)前言本篇文章将为大家讲解信号量,源码分析。在FreeRTOS中,信号量的实现基于队列。这种设计的思想是利用队列的特性来实现信号量,因为信号量可以被视为只能存储0或1个元素的特殊队列。在FreeRTOS中,二进制信号量(BinarySemaphore)通常由一个队列和一个计数器组成......
  • 基于微信小程序的酒店管理系统设计与实现(源码+lw+部署文档+讲解等)
    (文章目录)详细视频演示请联系我获取更详细的演示视频具体实现截图[外链图片转存中...(img-Lkna8qpn-1700727246356)]技术栈后端框架SpringBootSpringBoot内置了Tomcat、Jetty和Undertow等服务器,这意味着你可以直接使用它们而不需要额外的安装和配置。SpringBoot的一......
  • 基于springboot,vue的教务管理系统源码 学生信息管理系统
    项目源码获取方式放在文章末尾处项目技术数据库:Mysql5.7数据表:9张开发语言:Java(jdk1.8)开发工具:idea前端技术:Vue后端技术:SpringBoot 项目源码获取方式放在文章末尾处功能简介该项目是一个教务管理系统,角色分为管理员,教师,学生三个角色,具体功能菜单如下:管理员端    登录    ......
  • PHP医院手术麻醉信息系统源码,实现手术申请与排班、审批、安排、术前、术中和术后的信
    医院手术麻醉信息系统全套商业源码,自主版权,支持二次开发手术麻醉信息系统是HIS产品的中的一个组成部分,主要应用于医院的麻醉科,属于电子病历类产品。医院麻醉监护的功能覆盖整个手术与麻醉的全过程,包括手术申请与排班、审批、安排、术前、术中和术后的信息管理提供支持。手术麻醉信......