精华内容
下载资源
问答
  • muduo网络库

    热门讨论 2014-12-14 19:50:32
    muduo网络库,找了好久才找到的。学习只用,用完请删除。
  • muduo网络库/网络库/网络库.rar
  • Muduo is a multithreaded C++ network library ...muduo网络库是C++语言开发的一个非常优秀的网络库,作者陈硕,muduo网络库在多线程环境下性能非常高,该系列文章会分享一些muduo库源码的知识,包括它的线程池源码...

    Muduo is a multithreaded C++ network library based on the reactor pattern.
    muduo库的介绍就是:一个基于reactor反应堆模型的多线程C++网络库

    muduo网络库是C++语言开发的一个非常优秀的网络库,作者陈硕,muduo网络库在多线程环境下性能非常高,该系列文章会分享一些muduo库源码的知识,包括它的线程池源码,TcpServer源码,还有很多muduo库应用的示例代码等,学习一下作者优秀的C++多线程网络编程设计思想。

    muduo库源码github仓库地址
    https://github.com/chenshuo/muduo
    本文所使用muduo库源码云盘下载地址
    链接:https://pan.baidu.com/s/1Rqrnz8NY6UOiFemYQv-63Q 提取码:8nio

    这篇文章主要讲Linux平台下muduo网络库的源码编译安装,用来开发网络高并发程序。

    【注意】:muduo库是基于boost开发的,所以需要先在Linux平台上安装boost库,安装过程参考我的另一篇博客:https://blog.csdn.net/QIANGWEIYUAN/article/details/88792874

    1.拷贝muduo的源码压缩包muduo-master.zip到Linux系统下

    root@tony-virtual-machine:/home/tony/package# ls
    muduo-master.zip
    root@tony-virtual-machine:/home/tony/package# unzip muduo-master.zip
    

    解压完成后,进入muduo库的解压目录里面

    root@tony-virtual-machine:/home/tony/package# ls
    muduo-master  muduo-master.zip
    root@tony-virtual-machine:/home/tony/package# cd muduo-master
    root@tony-virtual-machine:/home/tony/package/muduo-master# ls
    armlinux.diff  build.sh   clang.diff      Doxyfile  License      muduo         README
    backport.diff  ChangeLog  CMakeLists.txt  examples  MacOSX.diff  premake4.lua  TODO
    

    注意,muduo库源码编译会编译很多unit_test测试用例代码,编译耗时长,我们也用不到,vim编辑上面源码目录里面的CMakeLists.txt文件,如下修改:
    在这里插入图片描述
    保存并退出,继续下面的步骤。

    2.看到有一个build.sh源码编译构建程序,运行该程序(注意:muduo是用cmake来构建的,需要先安装cmake,ubuntu下直接sudo apt-get install cmake就可以,redhat或者centos可以从yum仓库直接安装):

    拿ubuntu举例,如果没有安装cmake,执行下面的命令安装cmake:

    root@tony-virtual-machine:/home/tony/package/muduo-master# apt-get install cmake
    

    然后执行build.sh程序:

    root@tony-virtual-machine:/home/tony/package/muduo-master# ./build.sh
    

    最终100%编译完成:
    在这里插入图片描述

    3.编译完成后,在输入./build.sh install命令进行muduo库安装

    root@tony-virtual-machine:/home/tony/package/muduo-master# ./build.sh install
    

    这个./build.sh install实际上把muduo的头文件和lib库文件放到了muduo-master同级目录下的build目录下的release-install-cpp11文件夹下面了:

    root@tony-virtual-machine:/home/tony/package# ls
    build  muduo-master  muduo-master.zip
    root@tony-virtual-machine:/home/tony/package# cd build/
    root@tony-virtual-machine:/home/tony/package/build# ls
    release-cpp11  release-install-cpp11
    root@tony-virtual-machine:/home/tony/package/build# cd release-install-cpp11/
    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11# ls
    include  lib
    
    

    所以上面的install命令并没有把它们拷贝到系统路径下,导致我们每次编译程序都需要指定muduo库的头文件和库文件路径,很麻烦,所以我们选择直接把inlcude(头文件)和lib(库文件)目录下的文件拷贝到系统目录下:

    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11# ls
    include  lib
    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11# cd include/
    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11/include# ls
    muduo
    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11/include# mv muduo/ /usr/include/
    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11/include# cd ..
    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11# ls
    include  lib
    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11# cd lib/
    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11/lib# ls
    libmuduo_base.a  libmuduo_http.a  libmuduo_inspect.a  libmuduo_net.a
    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11/lib# mv * /usr/local/lib/
    root@tony-virtual-machine:/home/tony/package/build/release-install-cpp11/lib# 
    
    

    拷贝完成以后使用muduo库编写C++网络程序,不用在指定头文件和lib库文件路径信息了,因为g++会自动从/usr/include和/usr/local/lib路径下寻找所需要的文件。

    4.写测试代码,测试muduo是否能够正常使用,如下:
    把muduo库的头文件和lib库文件拷贝完成以后,使用muduo库编写一个简单的echo回显服务器,测试muduo库是否可以正常使用,代码如下:

    #include <muduo/net/TcpServer.h>
    #include <muduo/base/Logging.h>
    #include <boost/bind.hpp>
    #include <muduo/net/EventLoop.h>
    
    // 使用muduo开发回显服务器
    class EchoServer
    {
     public:
      EchoServer(muduo::net::EventLoop* loop,
                 const muduo::net::InetAddress& listenAddr);
    
      void start(); 
    
     private:
      void onConnection(const muduo::net::TcpConnectionPtr& conn);
    
      void onMessage(const muduo::net::TcpConnectionPtr& conn,
                     muduo::net::Buffer* buf,
                     muduo::Timestamp time);
    
      muduo::net::TcpServer server_;
    };
    
    EchoServer::EchoServer(muduo::net::EventLoop* loop,
                           const muduo::net::InetAddress& listenAddr)
      : server_(loop, listenAddr, "EchoServer")
    {
      server_.setConnectionCallback(
          boost::bind(&EchoServer::onConnection, this, _1));
      server_.setMessageCallback(
          boost::bind(&EchoServer::onMessage, this, _1, _2, _3));
    }
    
    void EchoServer::start()
    {
      server_.start();
    }
    
    void EchoServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
    {
      LOG_INFO << "EchoServer - " << 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)
    {
      // 接收到所有的消息,然后回显
      muduo::string msg(buf->retrieveAllAsString());
      LOG_INFO << conn->name() << " echo " << msg.size() << " bytes, "
               << "data received at " << time.toString();
      conn->send(msg);
    }
    
    
    int main()
    {
      LOG_INFO << "pid = " << getpid();
      muduo::net::EventLoop loop;
      muduo::net::InetAddress listenAddr(8888);
      EchoServer server(&loop, listenAddr);
      server.start();
      loop.loop();
    }
    

    使用g++进行编译,注意链接muduo和pthread的库文件,编译命令如下:

    g++ main.cpp -lmuduo_net -lmuduo_base -lpthread -std=c++11
    

    编译链接完成,生成a.out可执行程序,上面的echo服务器监听8888端口,运行上面的a.out回显服务器如下:

    root@tony-virtual-machine:/home/tony/code# ./a.out 
    20190404 08:00:15.254790Z 42660 INFO  pid = 42660 - main.cpp:61
    

    等待客户端连接,可以打开一个新的shell命令行用netcat命令模拟客户端连接echo服务器进行功能测试,命令如下:

    tony@tony-virtual-machine:~$ echo "hello world" | nc localhost 8888
    hello world
    
    

    客户端数据回显正确,看看服务器接日志信息打印如下:

    root@tony-virtual-machine:/home/tony/code# ./a.out 
    20190404 08:00:15.254790Z 42660 INFO  pid = 42660 - main.cpp:61
    20190404 08:00:59.438626Z 42660 INFO  TcpServer::newConnection [EchoServer] - new connection [EchoServer-0.0.0.0:8888#1] from 127.0.0.1:33480 - TcpServer.cc:80
    20190404 08:00:59.438707Z 42660 INFO  EchoServer - 127.0.0.1:33480 -> 127.0.0.1:8888 is UP - main.cpp:42
    20190404 08:00:59.438812Z 42660 INFO  EchoServer-0.0.0.0:8888#1 echo 12 bytes, data received at 1554364859.438723 - main.cpp:53
    
    

    到此,muduo安装成功,能够正常进行C++网络程序开发!

    展开全文
  • 完整muduo网络库

    2017-04-01 10:50:05
    完整版muduo网络库,仅供学习使用
  • muduo网络库设计总结

    千次阅读 2019-04-29 23:51:12
    文章目录muduo网络库简介muduo网络库模块组成Recator反应器EventLoop的两个组件TimerQueue定时器EventfdConnector和Acceptor连接器和监听器AcceptorConnectorTcpConnectionTcpServer和TcpClientmuduo中的线程安全...


    本篇结束muduo网络库部分学习的笔记,总结一下muduo网络库的模块组成,同时会提供笔记中个模块的实现代码,这些模块代码单独抽出同时去除了muduo中对boost的依赖,改用c++11中的组件或者用单独的类替换,会使得muduo的各个组件会更为简洁易学。
    基于C++11的muduo :https://github.com/BethlyRoseDaisley/SimpleMuduo 代码可能还有些bug和不完善的地方,简单使用和学习的话完全够用,也可自行完善。

    muduo网络库简介

    muduo是一个高质量的事件驱动型的网络库,其核心代码不超过4500行,使用的non-blocking IO(IO multiplexing)+ one loop per
    thread模型。此模型每个IO线程里面只有一个事件循环(即一个Reactor),处理读写和定时事件,激活的事件通过回调方式提供用户处理业务逻辑。
    在linux下的话,可以把事件当做一个文件描述符,换句话也就是说一个file descriptor只能由一个线程读写。
    一个线程最多只有一个EventLoop,而EventLoop中的循环即是在不停的监视这些描述符,当描述符可读或可写的时候,通过回调函数提供给用户处理。
    这样我们可以很方便地把不同的socket套接字的描述符放到不同的线程去, 也可以把一些socket放到一个线程里,这样这些socket就是线程安全的,因为始终只有EventLoo所在线程在读写它们,极大的降低了我们的编程复杂性。

    使用起来的话,它对外看上去应该这个样子,EventLoop一直处在事件循环中,通过IO复用机制select/poll/epoll回调激活的事件。

    muduo网络库模块组成

    muduo的组件大致可划分为 5个部分, Reactor、TimerQueue和Eventfd、Acceptor和Connector、TcpConnection、TcpServer和TcpClient。
    muduo网络部分的简化类图

    如果只注重服务端的话,可以TcpClient省去,Poller在muduo中是个纯虚基类,现在用poll(2)具体化它,省略它们后的结构应该是这样的。
    EventLoop和Poller及Channel组成Reactor部分、Acceptor作为TcpServer的监听器、TcpConnection负责处理socket的读写等事件、而TcpServer处理TcpConnection读写完成后的回调事件。

    Recator反应器

    Reactor由三部分组成,EventLoop、Poller、Channel.
    EventLoop即IO线程中的事件循环.它能确保所有注册的事件都在EventLoop对象所在的线程中执行,不用考虑事件的并发。它是线程安全的,且允许其他线程往EventLoop里面塞东西。

    Poller 是IO multiplexing的封装,它是EventLoop的组成,与EventLoop的生命期相当,为EventLoop提供poll()方法。

    Channel 每个Channel对象自始至终只负责一个文件描述符(fd) 的IO事件分发,但它不拥有这个fd,也不会在析构的时候关闭这个fd。每个Channel对象自始至终只属于一个EventLoop,因此每个Channel对象都只属于某一个IO线程。 Channel会把不同的IO事件分发为不同的回调, 例如ReadCallback、 WriteCallback等

    Reactor的实现笔记 muduo学习笔记(二)Reactor关键结构
    Reactor部分实现源码及简单测试 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/Reactor
    Reactor的时序图 :

    EventLoop的两个组件

    TimerQueue定时器

    TimerQueue 并未在类图中单独给出,它是EventLoop的组件,为EventLoop提供了定时任务,和周期任务的接口。通过注册一个Timerfd到Poller实现.

    TimerQueue的实现笔记 muduo网络库学习笔记(三)TimerQueue定时器队列
    TimerQueue实现部分源码及简单测试 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/TimerQueue

    Eventfd

    Eventfd 这个就是其他线程能往EventLoop线程里面塞任务的实现核心,它是 一个事件文件描述符fd,EventLoop通过将它注册到Poller,当其他线程往EventLoop里面塞任务的时候,先将任务存储在EventLoop的容器中,然后激活Eventfd,处理容器中存储的任务,当然赛任务需要一把锁来保护。

    Eventfd的实现笔记 muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制

    Connector和Acceptor连接器和监听器

    Acceptor

    Acceptor它是服务端TcpServer类的主要组件,封装服务端的连接监听部分,在非阻塞网络编程中,accept的描述符可读,表明有新的连接上来,连接建立后通过回调告知用户有新的连接上来。

    Acceptor的笔记 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor
    Acceptor实现部分源码及简单测试 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/Acceptor
    Acceptor的时序图 :

    Connector

    在非阻塞网络编程中,发起连接的基本方式是调用connect(2),当socket变得可写时表明连接建立完毕,但是其中要处理各种类型的错误,muduo中把它封装为Connector class.
    Connector 和 Acceptor 设计思路基本一致,只是Acceptor通过判断套接字是否可读来执行回调,而Connector是判断套接字是否可写来执行回调,但是要注意的是socket可写不一定就是连接建立好了 , 当连接建立出错时,套接口描述符变成既可读又可写,这时我们可以通过调用getsockopt来得到套接口上待处理的错误(SO_ERROR),如果错误是0表示连接成功。

    Connector的笔记 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor
    Connector实现部分源码及简单测试 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/Connector
    Connector的时序图 :

    TcpConnection

    Buffer muduo中的buffer通过vector和一个栈上空间实现,动态可调,其结构很精妙,感兴趣的话建议直接阅读陈硕Buffer部分设计的文章. 这个buffer主要做为TcpConnection的组件。
    Socket 封装一个套接字,管理了这个套接字描述符的生命期,是TcpConnection的组件,TcpConnection 通过这个套接字描述符注册读写事件,SocketHelp一个纯接口文件,封装了Socket的操作接口。
    TcpConnection 封装一条Tcp连接, 处理这条连接中的读写及错误,连接关闭等事件,这些事件会在TcpConnection的内部先进行处理,然后通过回调函数将TcpConnection缓冲的Buffer提供给用户处理。

    TcpServer和TcpClient

    Tcpserver 主要使用组件Acceptor,有新的连接到来时会new一个TcpConnection保存在ConnectionMaps(TcpConnection共享指针的一张映射表)中,通过创建时注册的名字索引管理所有的连接;有数据可读时通过MessgeCallBack回调提供用户使用。
    TcpClient 主要组件Connector, 它的实现与TcpServer相似,只不过每个TcpClient只管理一个TcpConnection。

    Reactor部分和EventLoop的组件理解后,TcpConnection和TcpServer部分就很好看懂了,所以也没有单独写新的文章。
    TcpServer实现部分源码及简单测试 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/TcpServer
    TcpClient实现部分源码及简单测试 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/TcpClient

    Tcpserver的时序图 :

    muduo中的线程安全日志

    AsyncLogging异步日志

    AsyncLogging一个C++Stream风格的多线程安全非阻塞日志,是muduo库中的另一个部分组成。
    这个日志使用了双缓冲机制,这样新建的日志不必等待磁盘操作,也避免了每条新的日志都触发日志线程,而是将多条日志拼程一个大的buffer 和后端buffer交换,后端线程就实时将后端buffer写入本地文件. 相当于批处理,减少线程唤醒频率 ,大大降低开销。
    另外 ,为了及时将 日志消息写入文件, 即是 buffer A 中还没有push进来日志 也会每周期执行一次上述的写入操作。
    但是有个问题,如果宕机,宕机瞬间缓存中的日志肯定是还没写完的。
    用了一阵子,总的来说,这个日志个人很喜欢,轻巧简洁,十分便利。
    AsyncLogging日志的格式化部分实现笔记 一个轻巧高效的多线程c++stream风格异步日志(一)
    AsyncLogging的双缓冲机制 一个轻巧高效的多线程c++stream风格异步日志(二)
    AsyncLogging的源码 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/AsyncLogging
    直接包含Logger.hh 和 AsycnLogging.hh即可直接使用。
    AsyncLogging类结构 :


    update at – 2019/4/29

    整理的muduo日志库和网路库 c++11版

    整理的muduo日志库和网路库 c++11版
    https://github.com/BethlyRoseDaisley/cpp11_muduo

    展开全文
  • Muduo网络库核心梳理

    2021-07-24 14:22:16
    Muduo网络库 Muduo网络库本身并不复杂,是一个新手入门C++面向对象网络编程的经典实战项目。但是,新手在刚刚上手读代码的时候,非常容易陷入代码的汪洋大海,迷失方向。本文旨在简要梳理Muduo网络库的核心内容,...

    Muduo网络库

    Muduo网络库本身并不复杂,是一个新手入门C++面向对象网络编程的经典实战项目。但是,新手在刚刚上手读代码的时候,非常容易陷入代码的汪洋大海,迷失方向。本文旨在简要梳理Muduo网络库的核心内容,帮助初学者快速上手源码阅读。

    第一部分:概述

    通过学习Muduo网络库的源码,初学者可以掌握的主要知识点有:

    • EventLoop事件循环的实现方式;
    • Reactor反应堆模式在TCP网络通信编程中的基本实现方式;(可以从《UNIX网络编程》中参考各种通信模型)
    • Socket API,Epoll/Poll 等I/O多路复用API的封装方式;(进一步探究需要读《UNIX网络编程》)
    • 多线程编程原语在Linux环境下的封装方式(仅针对pthread接口的封装,如果希望使用C++11 std::thread库实现的话可以跳过)
    • 日志类库,时间处理函数库,异步文件日志库等实用功能模块的基本实现方式;
    • CMake源码构建工具的使用;

    以上知识点,是建立在读者已经充分理解C++语言的基础上来讲的。在Muduo网络库中,大量使用了C++11的高级特性,例如智能指针std::unique_ptrstd::shared_ptr;lambda表达式,std::function仿函数;移动语义等等。如果读者对这些语言特性还未掌握,读源码的话会比较吃力,因此建议在语言学习基础充分扎实之后再来做源码的项目实践。但是总的来说,学习Muduo网络库对于C++面向对象编程的“继承,封装,多态”三大特性中的封装特性会有一个全面且深入的认识。

    然而在这Muduo的源码中,对于多态和继承特性的使用仅仅出现在了Poller类的处理上。Poller抽象类提供接口,并通过多态的方式实现了EpollPoller和PollPoller等多路复用类。初学者可以从Poller类及其派生类的实现方式,学习到如何利用多态,对接口和实现进行分离(面向对象的设计原则之一)。

    Muduo网络库的设计理念非常简单,就是为了用最简单的实现方式完成一个高性能、且功能完整的网络通信程序库,因此在设计上并没有采用过于复杂的语言特性,这是一个优点也是一个遗憾。如果读者在学习完Muduo网络库之后有更多想法,可以尝试通过继承和多态的特性重写Muduo网络库,使其更加灵活和可扩展。实际上,有许多开源的网络库就是这么做的,例如著名的C++ Web框架drogon中使用的trantor网络库,就是基于Muduo的设计进行重写的,加入了继承和多态的特性,有兴趣的可以自行研究trantor的源码。

    此外,Muduo网络库使用的CMake源码构建工具是非常出色的。它基本上完整地体现了一个标准工程项目应该具有的CMakeLists.txt文件的形式和书写方法。初学者可以用Muduo网络库学习并深入掌握CMake构建工具的使用方式。

    第二部分:核心类库梳理

    (细节持续更新)

    1. Reactor的核心机制:事件分发与事件循环

    EventLoop流程简图:
    在这里插入图片描述

    EventLoop

    一个事件循环,注意,一个创建了EventLoop对象的线程是IO线程

    • 主要接口

      • loop
        死循环,阻塞在Poller的poll函数,等待唤醒
        唤醒后执行ChannelList中每个Channel的回调
        最后执行任务队列中的Functor

      • runInLoop
        在IO线程中执行用户回调Functor,若调用者非IO线程,则会调用queueInLoop

      • queueInLoop
        当调用者并非当前EventLoop所在线程时,将Functor存入EventLoop的任务队列
        从而保证Functor由IO线程执行,这是线程安全的保证之一

      • updateChannel与removeChannel
        核心中的核心,通过这个公有接口建立起Channel和Poller沟通的桥梁
        Channel通过这个接口向Poller注册或者移除自己的fd
        实现了Poller和Channel两端的解耦

      • 核心实现:handleEvent
        遍历所有的activeChannelList_,并依次执行这些Channel中注册的回调函数
        这个环节非常非常关键,是一切事件派发机制中回调执行的地方

    • 主要成员

      • wakeupchannel_
        通过eventfd唤醒的channel
        EventLoop可以通过这个Channel唤醒自己执行定时任务
      • activeChannelList_
        通过一次poll获得的所有发生事件的Channel指针列表
      • pendingFunctors_
        所有非IO线程调用的用户回调都会存放在这个队列中,通过mutex互斥量保护
      • poller_
        一个多路复用实例

    Poller

    I/O多路复用接口(抽象)类,对I/O多路复用API的封装

    • 主要接口

      • poll
        是Poller的核心功能,使用派生类的poll或者epoll_wait来阻塞等待IO事件发生
        通过派生类的实现来填充EventLoop的activeChannelList_

      • static createNewPoller:
        工厂函数,创建一个Poller实例
        在EpollPoller中,每个实例对应一个epollfd

      • update
        更新I/O多路复用的状态,例如epoll_ctl的ADD,MOD,DEL

    • 主要成员

      • loop
        控制当前Poller的EventLoop指针
      • 其余成员由派生类实现

    Channel

    I/O事件派发器,封装了发生I/O事件的文件描述符,关心的事件类型,以及事件对应的回调

    • 主要接口

      • setter,getter
        设置关注读写操作,获取实际发生的读写事件

      • update
        根据回调的执行情况,通过EventLoop的updateChannel接口更新事件状态

    • 主要成员

      • loop
        Channel拥有者的EventLoop指针
        每个EventLoop可以控制若干个Channel
      • fd_
        这个Channel所维护的文件描述符
      • events_
        这个Channel所关心的IO事件
      • revents_
        这个Channel的文件描述符上实际发生的事情

    TimerQueue

    通过timerfd实现的定时器功能,为EventLoop扩展了一系列runAt,runEvery,runEvery等函数
    TimerQueue中通过std::set维护所有的Timer,也可以使用优先队列实现

    • 主要接口

      • addTimer
        向定时器中添加Timer
        Timer是一个封装了回调函数和时间的类
        通过内部实现addTimerInLoop保证线程安全
      • cancel
        从定时器中移除某个Timer
      • 核心实现:getExpired
        从timers_集合中移除已经到期的Timer
      • 核心实现:handleRead
        向timerfdChannel注册的回调函数
        在timerfd触发可读时间时,也就是定时器到期的时候执行
        会调用getExpired,并依次执行返回的所有Timer中的回调
    • 主要成员

      • timerfdChannel_
        用来唤醒计时器的Channel,维护了一个timerfd,注册了TimerQueue::handleRead回调
      • std::set<std::pair<Timestamp, Timer*>> timers_
        使用std::set容器来取得最近将要超时的Timer,从而决定是否resetTimerfd

    TimerQueue执行用户回调的时序图:
    TimerQueue时序图

    注意事项

    任何在IO线程(也就是具有EventLoop)中绑定的Channel中发生的IO事件,最终都会在EventLoop中的handleEvent环节依次处理。
    理解这一点非常重要,如果出问题需要debug,基本上都是在handleEvent环节中有线程安全的问题。

    其他辅助类

    • EventLoopThread
      封装了一个EventLoop的独立线程

    • EventLoopThreadPool
      封装了若干个EventLoopThread的线程池,所有者是一个外部的EventLoop

    • Timer
      定时器相关的类

    2. 网络通信封装

    TcpConnection

    封装了一个TCP链接

    • 主要接口

      • send
        发送数据的主要接口,最终通过内部实现在runInLoop中发送数据

      • 回调setter

      • connectionEstablished
        当连接建立时,应当只执行一次
        将自身的shared_from_this指针与Channel绑定
        令Channel激活对可读IO事件的关注

      • connectionDestroyed
        当连接断开时,应当只执行一次
        将自己的Channel从所属EventLoop中移除

    • 主要成员

      • loop
        主要回调都通过EventLoop所在线程处理

      • Channel
        通过Channel的回调调用自己的回调

      • Socket
        连接所属的套接字fd

      • localaddr,peeraddr
        本地和对端socketaddr

      • 各种回调callback

      • inputbuffer,outputbuffer
        应用层输入,输出缓冲区

    TcpClient

    客户端封装,需要注意的是,muduo的client类只负责一个连接

    • Connector - TcpClient逻辑上的内部类

      对于连接方法的封装

      • 主要接口

        • setNewConnectionCallback
          设置TcpClient交给的回调函数

        • start
          最后通过loop的runInLoop调用
          调用connect内部实现

        • stop
          最终通过loop的queueInLoop调用
          回收Channel控制的套接字(如果有的话)
          设置connect_标记为false

        • retry
          connect_标记为true,则重连

        • 核心实现:connect
          调用Socket::connect方法连接服务端
          连接成功后,创建一个Channel
          将自身的handleWrite回调注册到Channel上,并激活可写事件关注

        • 核心实现:handleWrite
          根据getSockError的情况决定调用创建连接回调,或是错误回调,或retry操作
          其中包含了TcpClient注册的回调newConnection

      • 主要成员

        • loop

        • channel
          unique_ptr指针,仅在连接建立时动态创建Channel对象
          当channel触发可写事件时,执行handleWrite
          并在handleWrite中执行TcpClient的newConnection

        • serverAddr

        • connector

        • connect_
          非常重要的一个标记,决定了是否retry

    • 主要接口

      • 回调setters
        这些回调函数会在新连接建立时,通过newConnection内部实现方法传递给TcpConnction对象
      • 核心实现:newConnection
        在构造时将这个函数作为回调注册给connector_对象
        在Connector中的Channel执行本回调后,创建一个新的TcpConnection对象
      • connect
        调用Connector的start接口
      • stop
        调用Connector的stop接口
    • 主要成员

      • loop
      • connector
        TcpClient所维护的一个连接器
      • retry_
      • TcpConnection connection_
        TcpClient所维护的一个TCP连接对象

    关于连接中回调的传递,参考下面的简图:
    TcpClient回调传递的示意

    TcpServer

    服务端封装 - muduo的server端维护了多个tcpconnection
    注意TcpServer本身不带Channel,而是使用Acceptor的Channel

    • Acceptor - 逻辑上的内部类

      接受器封装,实质上就是对Channel的多一层封装

      • 主要接口
        • listen
          监听连接
          当新连接进入时,调用Socket::accept创建套接字,触发TcpServer的回调
        • setNewConnectionCallback
          TcpServer通过该接口设置回调,当新连接套接字创建后,创建TcpConnection对象
        • 核心实现:
          通过socket::accept接受新连接,获得套接字fd
          这个fd作为参数调用TcpServer注册的回调
      • 主要成员
        • loop
        • channel
        • idlefd
          非常巧妙的设计,在服务器压力过大,无法新建文件描述符时,通过这个idlefd拒绝连接
          来自libevent的设计
    • 主要接口

      • 回调setters
        这些回调函数会在新连接建立时传递给TcpConnction对象
      • start
        启动threadPool_线程池
        在runInLoop中执行acceptor的listen
        这里专门设置了一个started_标记,防止多次运行start
      • 核心实现:newConnection
        从accept回调中获得的fd动态创建新的TcpConnection对象
        为连接对象注册各类回调函数
        将连接对象存入connectionMap_映射表里
    • 主要成员

      • loop
        这个loop也是acceptor的loop

      • acceptor

      • threadPool
        一个EventLoopThreadPool,用来存放io线程的EventLoopThread

      • socket
        服务端用来监听的socket

      • ConnectionMap
        一个连接名和实例(TcpConnectionPtr)的映射容器

    TcpServer中回调的传递示意简图:
    TcpServer回调简图

    Buffer

    封装了一个可变长的buffer,支持廉价的前插操作,以及内部挪腾操作避免额外申请空间

    3. Socket API封装

    Socket

    封装了一个sockfd

    SocketOps

    对socket设置API的封装

    InetAddress

    对sockaddr系列的封装

    Endian

    封装了字节序转换工具函数

    4. 基础类库封装

    Logging

    通用标准输出日志

    AsyncLogging

    异步文件日志

    Date

    日期类库封装

    展开全文
  • 383-muduo网络库编程

    2021-08-04 19:10:21
    muduo网络库编程 muduo网络库的底层就是epoll加linux的pthread线程库。 所以muduo库只能装在linux环境中。 muduo网络库的安装见我的另外一篇博客就可以。 我们在做项目的时候,服务器一定要做到高并发,要用到muduo...

    muduo网络库编程

    muduo网络库的底层就是epoll加linux的pthread线程库。
    所以muduo库只能装在linux环境中。

    muduo网络库的安装见我的另外一篇博客就可以。

    我们在做项目的时候,服务器一定要做到高并发,要用到muduo库。但是客户端只是向服务器请求服务,不需要高并发的性能要求。

    基于muduo的客户端服务器编程

    muduo网络库的编程很容易,要实现基于muduo网络库的服务器和客户端程序,只需要简单的组合TcpServer和TcpClient就可以,代码实现如下:
    服务器类的开发
    在这里插入图片描述

    客户端实现
    在这里插入图片描述

    用muduo中的线程池做计算任务

    采用muduo进行服务器编程,如果遇到需要开辟多线程单独来处理复杂的计算任务或者其它阻塞任务等,不需要直接调用pthread_create来创建线程,muduo库提供的ThreadPool线程池管理类已经把Linux的线程创建完全封装起来了,如果想研究源码,可以剖析muduo中ThreadPool.cc和Thread.cc。ThreadPool使用示例:
    在这里插入图片描述

    muduo的日志系统

    在开发软件产品过程中,日志的输出非常重要,可以记录很多软件运行过程中的信息,方便定位调试问题,跟踪统计信息等等,muduo库提供的日志级别有:
    在这里插入图片描述

    muduo网络库的多线程模型

    网络服务器编程常用模型
    【方案1】 : accept + read/write 不是并发服务器
    【方案2】 : accept + fork - process-pre-connection 适合并发连接数不大,计算任务工作量大于fork的开销
    【方案3】 :accept + thread thread-pre-connection 比方案2的开销小了一点,但是并发造成线程堆积过多
    【方案4】: muduo的网络设计:reactors in threads - one loop per thread方案的特点是one loop per thread,一个线程一个事件循环。有一个main reactor负载accept连接,然后把连接分发到某个sub reactor(采用round-robin(轮询)的方式来选择sub reactor),该连接的所用操作都在那个sub reactor所处的线程中完成。多个连接可能被分派到多个线程中,以充分利用CPU。Reactor poll的大小是固定的,根据CPU的数目确定。
    读写操作都在epoll上完成。

    //设置EventLoop的线程个数,底层通过EventLoopThreadPool线程池管理线程类EventLoopThread
    _server.setThreadNum(10);
    

    一个Base IO thread负责accept新的连接,接收到新的连接以后,使用轮询的方式在reactor pool中找到合适的sub reactor将这个连接挂载上去,这个连接上的所有任务都在这个sub reactor上完成。如果有过多的耗费CPU I/O的计算任务,可以提交到创建的ThreadPool线程池中专门处理耗时的计算任务。
    【方案5】 : reactors in process - one loop pre processnginx服务器的网络模块设计,基于进程设计,采用多个Reactors充当I/O进程和工作进程,通过一把accept锁,完美解决多个Reactors的“惊群现象”。

    我们要做到高并发

    在这里插入图片描述
    一般来说, 线程的数量和CPU的核数对等,做到高并发。I/O复用的好处就是一个线程可以监听多个套接字。对于连接量大活跃量少的场景。
    工作线程会单独开一个线程做耗时的I/O操作,传送文件,音频之类的,这样当前的工作线程就可以及时做其他的依然注册在epoll上的socket的读写事件。
    在这里插入图片描述

    muduo中的reactor模型

    reactor模型是什么?先看一下维基百科的标准解释:
    在这里插入图片描述
    从上面的描述,可以看出如下关键点:

    1. 事件驱动(event handling)
    2. 可以处理一个或多个输入源(one or more inputs)
    3. 通过Service Handler同步的将输入事件(Event)采用多路复用分发给相应的Request Handler(多个)处理

    在这里插入图片描述

    muduo网络库服务器编程示例

    首先,我们在vscode上,要链接的库有
    在这里插入图片描述
    我们依赖muduo库链接写的函数。
    链接的时候要这样写

    在这里插入图片描述
    如何去配置这些东西?
    我们打开vscode
    我们在test文件夹下创建一个新的文件夹:testmuduo
    在这里插入图片描述
    然后我们在testmuduo下定义一个文件:muduo_server.cpp

    /*
    muduo网络库给用户提供了两个主要的类
    TcpServer: 用于编写服务器程序的
    TcpClient: 用于编写客户端程序的
    
    epoll + 线程池
    好处:能够把网络I/O的代码和业务代码区分开
    业务代码就是:用户的连接和断开,用户的可读写事件
    我们只需要关注业务代码,什么时候发生和如何监听这些事情的发生由muduo库去做 
    */
    #include <muduo/net/TcpServer.h>
    #include <muduo/net/EventLoop.h>//事件循环 
    #include <iostream>
    #include <functional>//绑定器 
    #include <string>
    using namespace std;
    using namespace muduo;
    using namespace muduo::net;
    using namespace placeholders;
    //muduo的名字空间作用域
     
    /*
    基于muduo网络库开发服务器程序
    步骤如下: 
    1.组合TcpServer对象
    2.创建EventLoop事件循环对象的指针
    3.明确TcpServer构造函数需要什么参数,输出ChatServer的构造函数
    4.在当前服务器类的构造函数当中,注册处理连接的回调函数和处理读写时间的回调函数
    5.设置合适的服务端线程数量,muduo库会自己分配I/O线程和worker线程
    */
    class ChatServer//TCPServer 
    {
    public:
        ChatServer(EventLoop *loop,               //事件循环
                   const InetAddress &listenAddr, //muduo封装好的,绑定IP+Port
                   const string &nameArg)//给TCPserver一个名字 
            : _server(loop, listenAddr, nameArg), _loop(loop)//没有默认构造哦 
        {
            //给服务器注册用户连接的创建和断开的回调,回调就是对端的相应事件发生了告诉网络库 ,然后网络库告诉我 ,我在回调函数开发业务 
            _server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));//绑定this对象到这个方法中,_1是参数占位符 
    
            //给服务器注册用户读写事件的回调
            _server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));//绑定this对象到这个方法中 
    
            //设置服务器端的线程数量 1个I/O线程(监听新用户的连接事件), 3个worker线程
            //不设置的话,就1个线程而已,要处理连接又要处理业务 
            _server.setThreadNum(4);//设置4个线程,1个I/O线程,3个worker线程 
        }
    
        void start()//开启事件循环 
        {
            _server.start();
        }
    
    private:
        //专门处理:用户的连接创建和断开 epoll listenfd accept
        //如果有新用户的连接或者断开,muduo库就会调用这个函数 
        void onConnection(const TcpConnectionPtr &conn)
        {
            if (conn->connected())//连接 , peerAddress()对端的地址 localAddress() 本地的地址 
            {
                cout << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " state:online" << endl;
            }
            else//断开 
            {
                cout << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " state:offline" << endl;
                conn->shutdown();//相当于这些close(fd)
                //_loop->quit();
            }
        }
    
        //专门处理:用户的读写事件,muduo库去调用这个函数 
        void onMessage(const TcpConnectionPtr &conn, //连接,通过这个连接可以读写数据 
                       Buffer *buffer,               //缓冲区,提高数据收发的性能 
                       Timestamp time)               //接收到数据的时间信息
        {
            string buf = buffer->retrieveAllAsString();//收到的数据放到这个字符串中 
            cout << "recv data:" << buf << " time:" << time.toFormattedString() << endl;
            conn->send(buf);//返回 ,收到什么发送什么 
        }
    
        TcpServer _server;//第一步 
        EventLoop *_loop; //第二步相当于 epoll 事件循环的指针,有事件发生,loop上报 
    };
    
    int main()
    {
        EventLoop loop;//相当于像是创建了epoll
        InetAddress addr("127.0.0.1", 6000);//IP地址,端口号 
        ChatServer server(&loop, addr, "ChatServer");
    
        server.start();//listenfd通过 epoll_ctl 添加到 epoll 
        loop.loop();//相当于epoll_wait,以阻塞方式等待新用户连接,已连接用户的读写事件等
    }
    

    我们进行编译
    在这里插入图片描述
    我们再起一个终端 执行
    在这里插入图片描述
    连接了
    在这里插入图片描述
    在这里插入图片描述
    telnet是 ctrl和] 然后回车
    输入quit才退出

    在这里插入图片描述

    关于VScode的配置

    在这里插入图片描述
    在这里插入图片描述
    下面这两个路径是默认搜索的
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    ctrl+shift+B
    这样,直接build就可以了
    编译完成

    展开全文
  • muduo网络库架构总结

    2018-12-08 14:16:00
    目录 muduo网络库简介 muduo网络库模块组成 Recator反应器 EventLoop的两个组件 TimerQueue定时器 Eventfd Connector和Acceptor连接器和监听器 Accepto...
  • 从实例看muduo网络库各模块交互过程

    多人点赞 2021-09-18 22:16:31
    文章目录muduo网络库的核心代码模块各模块功能解释ChannelPollerEpollPollerEventLoopEventLoopThreadEventLoopThreadPoolTcpServerTcpConnection从实际应用出发 muduo网络库的核心代码模块 1、channel 2、Poller 和...
  • muduo网络库使用

    千次阅读 2016-06-02 15:15:40
    现在结合muduo网络库来学习下C++的web server。 一.环境搭建 首先是muduo网络库的安装和环境搭建: 可参见http://blog.csdn.net/Solstice/article/details/5848547 二.echo程序的编译运行 muduo网络库是给出了很多的...
  • muduo网络库浅谈(二)第二章 muduo网络库之网络通信底层库的实现namespace socketsclass InetAddressclass Socketclass Accetporclass TcpServerclass TcpConnection 第二章 muduo网络库之网络通信 muduo网络库的...
  • muduo网络库介绍 我觉得我的介绍肯定是没有陈硕大神介绍的完整,所以我这里直接贴上陈硕的原文链接:muduo网络库 在这里,我将只讨论muduo的使用,后续源码剖析可以关注专栏:muduo网络库 使用muduo进行server端编程...
  • muduo网络库的编程很容易,要实现基于muduo网络库的服务器和客户端程序,只需要简单的组合 TcpServer 和 TcpClient 就可以。 所以,我建议,这个系列整完去看一下源码。 网络服务器编程常用模型 【方案1】 : ...
  • muduo网络库之Poller类

    2020-08-05 11:21:44
    工作原理:muduo网络库中Poller类是一个抽象类,用户使用PollPoller或者EPollPoller类。Poller类管理这一个文件描述符fd和Channel指针的映射表channels_,同一个IO线程内所有用户注册的文件描述符都保存在这里,包括...
  • muduo网络库设计与实现(一) 文章目录muduo网络库设计与实现(一)basenoncopyableMutexConditionCountDownLatchCurrentThreadThreadReactor模型muduo多线程模型EventLoopChannelEpollerTimerQueueEventLoopThread 这是...
  • 文章来源:muduo网络库预备知识点 前面都在分析muduo/base中的源码,这些是辅助网络库的。在分析网络库前,先总结一下相关知识点。 TCP网络编程要关注哪些问题?muduo网络库总结为三个半事件。 TCP网络...
  • muduo网络库浅谈
  • 前几天忙了下科研的事情,抽出空来继续学习一下muduo,本章开始正式学习muduo网络库,一点点深入linux多线程服务器的内部。好了,加油,开始了。 TCP编程本质 基于事件的非阻塞网络编程是高性能并发网络服务器的主流...
  • VS2017调试muduo网络库

    千次阅读 2019-01-16 17:04:43
    VS2017调试muduo网络库准备工作下载muduo源码配置linux环境VS打开工程开始调试待续 准备工作 下载muduo源码 muduo的github地址:https://github.com/chenshuo/muduo 进入release页面下载2.0.0版本的zip文件,然后...
  • Muduo网络库简介 muduo 是一个基于 Reactor 模式的现代 C++ 网络库,作者陈硕。它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。 muduo网络库的核心代码...
  • Muduo网络库简介 muduo 是一个基于 Reactor 模式的现代 C++ 网络库,作者陈硕。它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。 muduo网络库的核心代码...
  • muduo网络库——日志

    2020-08-02 12:07:54
    muduo网络库也不例外。 日志级别 日志一般有5个正常级别: WARN 警告信息表明潜在错误。 INFO打印重要流程信息 ERROR错误,不影响软件正常进行 FATAL影响程序执行 DEBUG 调试信息 另外,还有两个可用的特别的日志...
  • Muduo网络库简介 muduo 是一个基于 Reactor 模式的现代 C++ 网络库,作者陈硕。它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。 muduo网络库的核心代码...
  • Muduo网络库简介 muduo 是一个基于 Reactor 模式的现代 C++ 网络库,作者陈硕。它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。 muduo网络库的核心代码...
  • muduo网络库源码阅读Step by Step 一般写服务端程序都需要有一个称手的网络库来帮我们处理琐碎的网络通信细节,比如连接的建立、关闭,读取数据,发送数据,接收、发送缓冲区的管理等,常用的C/C++网络库有libevent...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,433
精华内容 1,773
关键字:

muduo网络库