原来Muduo是一个人自己写的!!!

本文主要介绍Muduo网络库的使用,包括其设计目标、下载与编译方法、基本结构和核心类功能。Muduo是一个基于Reactor模式的网络库,适用于多线程环境,支持TCP协议,且提供了丰富的示例供开发者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

陈硕 (giantchen_AT_gmail)
Blog.youkuaiyun.com/Solstice
2010 Aug 30

本文主要介绍 muduo 网络库的使用。其设计与实现将有另文讲解。

由来
半年前我写了一篇《 学之者生,用之者死——ACE历史与简评》,其中提到“我心目中理想的网络库”的样子:
  •         线程安全,支持多核多线程
  •         不考虑可移植性,不跨平台,只支持 Linux,不支持 Windows。
  •         在不增加复杂度的前提下可以支持 FreeBSD/Darwin,方便将来用 Mac 作为开发用机,但不为它做性能优化。也就是说 IO multiplexing 使用 poll 和 epoll。
  •         主要支持 x86-64,兼顾 IA32
  •         不支持 UDP,只支持 TCP
  •         不支持 IPv6,只支持 IPv4
  •         不考虑广域网应用,只考虑局域网
  •         只支持一种使用模式:non-blocking IO + one event loop per thread,不考虑阻塞 IO
  •         API 简单易用,只暴露具体类和标准库里的类,不使用 non-trivial templates,也不使用虚函数
  •         只满足常用需求的 90%,不面面俱到,必要的时候以 app 来适应 lib
  •         只做 library,不做成 framework
  •         争取全部代码在 5000 行以内(不含测试)
  •         以上条件都满足时,可以考虑搭配 Google Protocol Buffers RPC

在想清楚这些目标之后,我开始第三次尝试编写自己的 C++ 网络库。与前两次不同,这次我一开始就想好了库的名字,叫 muduo (木铎),并在 Google code 上创建了项目:http://code.google.com/p/muduo/ 。muduo 的主体内容在 5 月底已经基本完成,现在我把它开源。
本文主要介绍 muduo 网络库的使用,其设计与实现将有另文讲解。

下载与编译

下载地址: http://muduo.googlecode.com/files/muduo-0.1.0-alpha.tar.gz
SHA1 Checksum: 5d3642e311177ded89ed0d15c10921738f8c984c
Muduo 使用了 Linux 较新的系统调用,要求 Linux 的内核版本大于 2.6.28 (我自己用的是 2.6.32 )。在 Debian Squeeze / Ubuntu 10.04 LTS 上编译测试通过,32 位和 64 位系统都能使用。
Muduo 采用 CMake 为 build system,安装方法:
  1. $ sudo apt-get install cmake
复制代码
Muduo 依赖 Boost,很容易安装:
  1. $ sudo apt-get install libboost1.40-dev # 或 libboost1.42-dev
复制代码
编译方法很简单:
  1. $ tar zxf muduo-0.1.0-alpha.tar.gz
  2. $ cd muduo/
  3. $ ./build.sh
  4. # 编译生成的可执行文件和静态库文件分别位于 ../build/debug/{bin,lib}
复制代码
如果要编译 release 版,可执行
  1. $ BUILD_TYPE=release ./build.sh
  2. # 编译生成的可执行文件和静态库文件分别位于 ../build/release/{bin,lib}
复制代码
编译完成之后请试运行其中的例子。比如 bin/inspector_test ,然后通过浏览器访问 http://10.0.0.10:12345/ 或 http://10.0.0.10:12345/proc/status,其中 10.0.0.10 替换为你的 Linux box 的 IP。

例子

Muduo 附带了几十个小例子,位于 examples 目录。其中包括从 Boost.Asio、JBoss Netty、Python Twisted 等处移植过来的例子。
  1. examples
  2. |-- simple          # 简单网络协议的实现
  3. |   |-- allinone    # 在一个程序里同时实现下面 5 个协议
  4. |   |-- chargen     # RFC 864,可测试带宽
  5. |   |-- daytime     # RFC 867
  6. |   |-- discard     # RFC 863
  7. |   |-- echo        # RFC 862
  8. |   |-- time        # RFC 868
  9. |   `-- timeclient  # time 协议的客户端
  10. |-- hub             # 一个简单的 pub/sub/hub 服务,演示应用级的广播
  11. |-- roundtrip       # 测试两台机器的网络延时与时间差
  12. |-- asio            # 从 Boost.Asio 移植的例子
  13. |   |-- chat        # 聊天服务
  14. |   `-- tutorial    # 一系列 timers
  15. |-- netty           # 从 JBoss Netty 移植的例子
  16. |   |-- discard     # 可用于测试带宽,服务器可多线程运行
  17. |   |-- echo        # 可用于测试带宽,服务器可多线程运行
  18. |   `-- uptime      # TCP 长连接
  19. `-- twisted         # 从 Python Twisted 移植的例子
  20.     `-- finger      # finger01 ~ 07
复制代码
基本结构

Muduo 的目录结构如下。
  1. muduo
  2. |-- base            # 与网络无关的基础代码,已提前发布
  3. `-- net             # 网络库
  4.     |-- http        # 一个简单的可嵌入的 web 服务器
  5.     |-- inspect     # 基于以上 web 服务器的“窥探器”,用于报告进程的状态
  6.     `-- poller      # poll(2) 和 epoll(4) 两种 IO multiplexing 后端
复制代码
Muduo 是基于 Reactor 模式的网络库,其核心是个事件循环 EventLoop,用于响应计时器和 IO 事件。Muduo 采用基于对象(object based)而非面向对象(object oriented)的设计风格,其接口多以 boost::function + boost::bind 表达。
Muduo 的头文件明确分为客户可见和客户不可见两类。客户可见的为白底,客户不可见的为灰底。
muduo_inc.png
这里简单介绍各个头文件及 class 的作用,详细的介绍留给以后的博客。

公开接口
  •         Buffer 仿 Netty ChannelBuffer 的 buffer class,数据的读写透过 buffer 进行
  •         InetAddress 封装 IPv4 地址 (end point),注意,muduo 目前不能解析域名,只认 IP
  •         EventLoop 反应器 Reactor,用户可以注册计时器回调
  •         EventLoopThread 启动一个线程,在其中运行 EventLoop::loop()
  •         TcpConnection 整个网络库的核心,封装一次 TCP 连接
  •         TcpClient 用于编写网络客户端,能发起连接,并且有重试功能
  •         TcpServer 用于编写网络服务器,接受客户的连接
  •         在这些类中,TcpConnection 的生命期依靠 shared_ptr 控制(即用户和库共同控制)。Buffer 的生命期由 TcpConnection 控制。其余类的生命期由用户控制。
  •         HttpServer 和 Inspector,暴露出一个 http 界面,用于监控进程的状态,类似于 Java JMX。这么做的原因是,《程序员修炼之道》第 6 章第 34 条提到“对于更大、更复杂的服务器代码,提供其操作的内部试图的一种漂亮技术是使用内建的 Web 服务器”,Jeff Dean 也说“(每个 Google 的服务器进程)Export HTML-based status pages for easy diagnosis”。


内部实现

  •         Channel 是 selectable IO channel,负责注册与响应 IO 事件,它不拥有 file descriptor。它是 Acceptor、Connector、EventLoop、TimerQueue、TcpConnection 的成员,生命期由后者控制。
  •         Socket 封装一个 file descriptor,并在析构时关闭 fd。它是 Acceptor、TcpConnection 的成员,生命期由后者控制。EventLoop、TimerQueue 也拥有 fd,但是不封装为 Socket。
  •         SocketsOps 封装各种 sockets 系统调用。
  •         EventLoop 封装事件循环,也是事件分派的中心。它用 eventfd(2) 来异步唤醒,这有别于传统的用一对 pipe(2) 的办法。它用 TimerQueue 作为计时器管理,用 Poller 作为 IO Multiplexing。
  •         Poller 是 PollPoller 和 EPollPoller 的基类,采用“电平触发”的语意。它是 EventLoop 的成员,生命期由后者控制。
  •         PollPoller 和 EPollPoller 封装 poll(2) 和 epoll(4) 两种 IO Multiplexing 后端。Poll 的存在价值是便于调试,因为 poll(2) 调用是上下文无关的,用 strace 很容易知道库的行为是否正确。
  •         Connector 用于发起 TCP 连接,它是 TcpClient 的成员,生命期由后者控制。
  •         Acceptor 用于接受 TCP 连接,它是 TcpServer 的成员,生命期由后者控制。
  •         TimerQueue 用 timerfd 实现定时,这有别于传统的设置 poll/epoll_wait 的等待时长的办法。为了简单起见,目前用链表来管理 Timer,如果有必要可改为优先队列,这样复杂度可从 O(n) 降为 O(1)。它是 EventLoop 的成员,生命期由后者控制。
  •         EventLoopThreadPool 用于创建 IO 线程池,也就是说把 TcpConnection 分派到一组运行 EventLoop 的线程上。它是 TcpServer 的成员,生命期由后者控制。


线程模型

Muduo 的线程模型符合我主张的 one loop per thread + thread pool 模型。每个线程最多有一个 EventLoop。每个 TcpConnection 必须归某个 EventLoop 管理,所有的 IO 会转移到这个线程,换句话说一个 file descriptor 只能由一个线程读写。TcpConnection 所在的线程由其所属的 EventLoop 决定,这样我们可以很方便地把不同的 TCP 连接放到不同的线程去,也可以把一些 TCP 连接放到一个线程里。TcpConnection 和 EventLoop 是线程安全的,可以跨线程调用。TcpServer 直接支持多线程,它有两种模式:
  • 单线程,accept 与 TcpConnection 用同一个线程做 IO。
  • 多线程,accept 与 EventLoop 在同一个线程,另外创建一个 EventLoopThreadPool,新到的连接会按 round-robin 方式分配到线程池中。


结语
Muduo 是我对常见网络编程任务的总结,用它我能很容易地编写多线程的 TCP 服务器和客户端。Muduo 是我业余时间的作品,代码估计还有很多 bug,功能也不完善(例如不支持 signal 处理),待日后慢慢改进吧。
### muduo Network Library 中 Timestamp 类文档与使用示例 #### 定义与描述 `Timestamp` 是 `muduo` 库中的一个时间戳类,用于表示精确到微秒的时间点。此类提供了多种方法来创建、比较以及格式化时间戳对象[^1]。 ```cpp #include <muduo/base/Timestamp.h> ``` #### 构造函数 可以利用无参构造函数初始化当前时刻的时间戳;也可以通过传递自 Unix 纪元以来经过的微妙数来显式设置特定的时间点。 - 默认构造函数:创建代表当前时间的时间戳实例。 ```cpp Timestamp(); ``` - 带参数构造函数:基于给定的微秒数值构建时间戳 ```cpp explicit Timestamp(int64_t microSecondsSinceEpoch); ``` #### 成员函数 提供了一系列成员函数以便于操作和获取有关时间的信息: - 获取距离纪元起始点所经历过的微妙数量 ```cpp int64_t microsecondsSinceEpoch() const; ``` - 将时间转换成类可读的形式 (YYYY-MM-DD hh:mm:ss),并返回字符串形式的结果 ```cpp std::string toString() const; ``` - 返回更简洁版本的类可阅读日期时间表达方式(不带毫秒部分) ```cpp std::string toFormattedString(bool showMicroseconds = false) const; ``` - 静态工厂方法用来获得现在这一刻的新建时间戳对象 ```cpp static Timestamp now(); ``` - 计算两个时间差值,并以秒为单位给出结果 ```cpp double timeDifference(Timestamp high, Timestamp low); ``` - 从本地时区解析由 C-style 字符串表示的标准 ISO8601 时间格式 (%Y-%m-%d %H:%M:%S 或者带有 .u 的变体), 并生成对应的时间戳 ```cpp static Timestamp fromUnixTime(time_t seconds, int microseconds = 0); ``` #### 使用案例 下面是一些简单的例子展示如何运用这些功能: ```cpp // 创建一个新的时间戳表示此刻 Timestamp tsNow = Timestamp::now(); // 输出标准格式化的当前时间 LOG_INFO << tsNow.toFormattedString(true); // 手动定义某个过去/未来的时间点 Timestamp customTs(1577836800 * static_cast<int64_t>(1e6)); // UTC Jan 1st, 2020 @ midnight LOG_INFO << "Custom timestamp represents: " << customTs.toString(); ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值