Muduo 网络编程示例之七:“串并转换”连接服务器及其自动化测试

简介:

Muduo 网络编程示例之七:连接服务器及其自动化测试

陈硕 (giantchen_AT_gmail)

Blog.csdn.net/Solstice  t.sina.com.cn/giantchen

这是《Muduo 网络编程示例》系列的第七篇文章。

本文介绍如何使用 test harness 来测试一个具有内部逻辑的网络服务程序。

本文的代码见 http://code.google.com/p/muduo/source/browse/trunk/examples/multiplexer

云风在他的博客中提到了网游连接服务器的功能需求(搜“练手项目”),我用 C++ 初步实现了这些需求,并为之编写了配套的自动化 test harness,作为 muduo 网络库的示例。

注意:本文呈现的代码仅仅实现了基本的功能需求,没有考虑安全性,也没有特别优化性能,不适合用作真正的放在公网上运行的网游连接服务器。

功能需求

这个连接服务器把多个客户连接汇聚为一个内部 TCP 连接,起到“数据串并转换”的作用,让 backend 的逻辑服务器专心处理业务,而无需顾及多连接的并发性。以下是系统的框图:

multiplexer

这个连接服务器的作用与数字电路中的数据选择器 (multiplexer) 类似,所以我把它命名为 multiplexer。(其实 IO-Multiplexing 也是取的这个意思,让一个 thread-of-control 能有选择地处理多个 IO 文件描述符。)

mux

(上图取自 wikipedia,是 public domain 版权)

实现

Multiplexer 的功能需求不复杂,无非是在 backend connection 和 client connections 之间倒腾数据。具体来说,主要是处理四种事件:

由上可见,multiplexer 的功能与 proxy 颇为类似。multiplexer_simple.cc 是一个单线程版的实现,借助 muduo 的 io-multiplexing 特性,可以方便地处理多个并发连接。

在实现的时候有两点值得注意:

  • TcpConnection 的 id 如何存放?当从 backend 收到数据,如何根据 id 找到对应的 client connection?当从 client connection 收到数据,如何得知其 id ?

第一个问题比较好解决,用 std::map〈int, TcpConnectionPtr〉 clientConns_; 保存从 id 到 client connection 的映射就行。

第二个问题固然可以用类似的办法解决,但是我想借此介绍一下 muduo::net::TcpConnection 的 context 功能。每个 TcpConnection 都有一个 boost::any 成员,可由客户代码自由支配(get/set),代码如下。这个 boost::any 是 TcpConnection 的 context,可以用于保存与 connection 绑定的任意数据(比方说 connection id、connection 的最后数据到达时间、connection 所代表的用户的名字等等)。这样客户代码不必继承 TcpConnection 就能 attach 自己的状态,而且也用不着 TcpConnectionFactory 了(如果允许继承,那么必然要向 TcpServer 注入此 factory)。

class TcpConnection : public boost::enable_shared_from_this,
                      boost::noncopyable
{
 public:

  void setContext(const boost::any& context)
  { context_ = context; }

  boost::any& getContext()
  { return context_; }

  const boost::any& getContext() const
  { return context_; }

  // ...

 private:
  // ...
  boost::any context_;
};

typedef boost::shared_ptr TcpConnectionPtr;

对于 Multiplexer,在 onClientConnection() 里调用 conn->setContext(id),把 id 存到 TcpConnection 对象中。onClientMessage() 从 TcpConnection 对象中取得 id,连同数据一起发送给 backend,完整实现如下:

  void onClientMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)
  {
    if (!conn->getContext().empty())
    {
      int id = boost::any_cast(conn->getContext());
      sendBackendBuffer(id, buf);
    }
    else
    {
      buf->retrieveAll();
    }
  }
  • TcpConnection 的生命期如何管理?由于 Client Connection 是动态创建并销毁,其生与灭完全由客户决定,如何保证 backend 想向它发送数据的时候,这个 TcpConnection 对象还活着?解决思路是用 reference counting,当然,不用自己写,用 boost::shared_ptr 即可。TcpConnection 是 muduo 中唯一默认采用 shared_ptr 来管理生命期的对象,盖由其动态生命期的本质决定。更多内容请参考陈硕《当析构函数遇到多线程──C++ 中线程安全的对象回调

multiplexer 是二进制协议,如何测试呢?

自动化测试

Multiplexer 是 muduo 网络编程示例中第一个具有 non-trivial 业务逻辑的网络程序,根据陈硕《分布式程序的自动化回归测试》一文的思想,我为它编写了 test harness。代码见 http://code.google.com/p/muduo/source/browse/trunk/examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer

这个 Test harness 采用 Java 编写,用的是 Netty 库。这个 test harness 要扮演 clients 和 backend,也就是既要主动发起连接,也要被动接受连接。而且,test harness 与 multiplexer 的启动顺序是任意的,如何做到这一点请阅读代码。结构如下:

harness

Test harness 会把各种 event 汇聚到一个 blocking queue 里边,方便编写 test case。Test case 则操纵 test harness,发起连接、发送数据、检查收到的数据,例如以下是其中一个 test case

http://code.google.com/p/muduo/source/browse/trunk/examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestOneClientSend.java

这里的几个 test cases 都以用 java 直接写的,如果有必要,也可以采用 Groovy 来编写,这样可以在不重启 test harness 的情况下随时修改添加 test cases。具体做法见陈硕《“过家家”版的移动离线计费系统实现》。

将来的改进

有了这个自动化的 test harness,我们可以比较方便且安全地修改(甚至重新设计)multiplexer。例如

  • 增加“backend 发送指令断开 client connection”的功能。有了自动化测试,这个新功能可以被单独测试(指开发者测试),而不需要真正的 backend 参与进来。
  • 将 Multiplexer 改用多线程重写。有了自动化回归测试,我们不用担心破坏原有的功能,可以放心大胆地重写。而且由于 test harness 是从外部测试,不是单元测试,重写 multiplexer 的时候不用动 test cases,这样保证了测试的稳定性。另外,这个 test harness 稍作改进还可以进行 stress testing,既可用于验证多线程 multiplexer 的正确性,亦可对比其相对单线程版的效率提升。




   本文转自 陈硕  博客园博客,原文链接:http://www.cnblogs.com/Solstice/archive/2011/05/02/2034533.html,如需转载请自行联系原作者




相关文章
|
11月前
|
运维 Prometheus 监控
3 年部署经验总结:用自动化工具轻松管理 300+ 服务器开源软件
三年前接手公司IT部门时,我满怀信心,却发现部署效率低下。尽管使用了GitLab、Jenkins、Zabbix等100+开源工具,部署仍耗时费力。文档厚重如百科,却难解实际困境。一次凌晨三点的加班让我下定决心改变现状。偶然看到一篇国外博客,介绍了自动化部署的高效方式,我深受启发。
403 0
|
11月前
|
存储 弹性计算 网络协议
阿里云服务器ECS实例规格族是什么?不同规格CPU型号、处理器主频及网络性能参数均不同
阿里云ECS实例规格族是指具有不同性能特点和适用场景的实例类型集合。不同规格族如计算型c9i、通用算力型u1、经济型e等,在CPU型号、主频、网络性能、云盘IOPS等方面存在差异。即使CPU和内存配置相同,性能参数和价格也各不相同,适用于不同业务需求。
767 144
|
存储 运维 API
HPE OneView 10.0 - HPE 服务器、存储和网络设备集中管理软件
HPE OneView 10.0 - HPE 服务器、存储和网络设备集中管理软件
277 1
|
JSON 监控 API
在线网络PING接口检测服务器连通状态免费API教程
接口盒子提供免费PING检测API,可测试域名或IP的连通性与响应速度,支持指定地域节点,适用于服务器运维和网络监控。
2114 0
|
机器学习/深度学习 存储 监控
内部文件审计:企业文件服务器审计对网络安全提升有哪些帮助?
企业文件服务器审计是保障信息安全、确保合规的关键措施。DataSecurity Plus 是由卓豪ManageEngine推出的审计工具,提供全面的文件访问监控、实时异常告警、用户行为分析及合规报告生成功能,助力企业防范数据泄露风险,满足GDPR、等保等多项合规要求,为企业的稳健发展保驾护航。
365 0
|
JavaScript 数据可视化 Docker
简易制作MCP服务器并测试
本文介绍了如何简易制作并测试MCP服务器,包括环境搭建、代码实现及Docker部署。首先通过uv包创建项目,在main.py中定义MCP服务器及其工具和资源函数。接着详细说明了在Windows上安装uv、配置Docker镜像加速、生成requirements.txt文件以及编写Dockerfile的过程。最后,通过构建和运行Docker容器部署MCP服务器,并使用Node.js工具测试其功能,确保服务器正常工作。此教程适合初学者快速上手MCP服务器的开发与部署。
5024 63
|
安全
基于Reactor模式的高性能服务器之Acceptor组件(处理连接)
本节介绍了对底层 Socket 进行封装的设计与实现,通过 `Socket` 类隐藏系统调用细节,提供简洁、安全、可读性强的接口。重点包括 `Socket` 类的核心作用(管理 `sockfd_`)、成员函数的功能(如绑定地址、监听、接受连接等),以及 `Acceptor` 组件的职责:监听连接、接收新客户端连接并分发给上层处理。同时说明了 `Acceptor` 与 `EventLoop` 和 `TcpServer` 的协作关系,并展示了其成员变量和关键函数的工作机制。
264 2
|
11月前
|
存储 监控 Linux
Dell OpenManage Enterprise 4.5 - Dell 服务器、存储和网络设备集中管理软件
Dell OpenManage Enterprise 4.5 - Dell 服务器、存储和网络设备集中管理软件
312 0