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

快速上手 muduo

以词典服务与客户端为例的实战教程。

1. 词典服务端设计
1.1 服务端架构设计

TcpServer 是 Muduo 库中用于创建 TCP 服务器的核心类,它封装了服务端的监听、连接管理、数据收发等功能;

EventLoop 是 Muduo 库中最重要的类之一,它负责管理和调度所有 I/O 事件。

简单来说,TcpServer 类负责发起各种任务,这些任务通常被封装成回调函数;EventLoop 负责处理各种被触发的任务。

#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>class DictServer
{
private:muduo::net::EventLoop _baseloop;muduo::net:TcpServer _server;
};
1.2 构造 DictServer

muduo::net::TcpServer 的构造函数:

#include <muduo/net/TcpServer.h>TcpServer(EventLoop* loop,const InetAddress& listenAddr,const string& nameArg,Option option = kNoReusePort);
// EventLoop 事件循环监控
// InetAddress 监听套接字
// Option 设置端口复用
public:DictServer(uint16_t port):_server(&_baseloop, InetAddress("0.0.0.0", port), "DictServer", kReusePort){}
1.3 设置连接事件回调函数

连接事件回调函数 用于处理客户端和服务器之间的连接建立和断开事件;

通过设置这些回调函数,可以在连接状态发生变化时执行响应的操作。

#include <muduo/net/TcpConnection.h>class DictServer
{
private:void onConnection(const muduo::net::TcpConnectionPtr& conn){if (conn->connected()) {std::cout << "连接成功" << std::endl;}else  {std::cout << "连接断开" << std::endl;}}
public:DictServer(uint16_t port):_server(&_baseloop, InetAddress("0.0.0.0", port), "DictServer", kReusePort){// 设置连接事件回调函数_server.setConnectionCallback(std::bind(&DictServer::onConnection, this, std::placeholders::_1));}
};
1.4 设置消息事件回调函数

Buffer 是 Muduo 库中用于处理网络数据传输的一个重要类;

它提供了一个高效的缓冲区管理机制,用于存储和操作从网络接收或准备发送的数据。

retrieveAllAsString() —— 提取缓存区内的所有数据,作为一个字符串。

对于服务器而言,消息事件回调函数 用于处理客户端发送的信息;

通过设置这些回调函数,可以在接收到客户端消息后执行相应的操作。

#include <muduo/net/Buffer.h>
#include <string>
#include <unordered_map>std::unordered_map<std::string, std::string> dict{{"string", "字符串"},{"iterator", "迭代器"},{"callback", "回调"}
}class DictServer
{
private:void onMessage(const muduo::net::TcpConnectionPtr& conn, Buffer* buf){// messageCallback 通常在客户端已经成功连接到服务器,并且有数据到达时才被调用;// 因此大多数情况下,在消息事件回调函数处理接收到的数据时,客户端连接应该已经建立了。// if (!conn->connected()) {} // 为了增加代码的健壮性,可以将这部分内容实现std::string str = buf->retrieveAllAsString();std::string res;if (dict.find(str) != dict.end())res = dict[str];else ret = "not found";conn->send(res);}
public:DictServer(uint16_t port):_server(&_baseloop, InetAddress("0.0.0.0", port), "DictServer", kReusePort){// 设置连接事件回调函数_server.setConnectionCallback(std::bind(&DictServer::onConnection, this, std::placeholders::_1));// 设置消息事件回调函数_server.setMessageCallback(std::bind(&DictServer::onMessage, this, std::placeholders::_1,std::placeholders::_2));}
};
1.5 服务启动
class DictServer {
public:void Start() {_server.start(); // 开始监听连接_baseloop.loop(); // 开始事件循环监控}
};

loop 方法中,_baseloop 会不断监听并执行各种回调函数:

  • 当有新的连接请求,_baseloop 会执行连接事件回调函数;
  • 当有新的数据到达,_baseloop 会执行消息事件回调函数。
2. 词典客户端设计

依据上述知识,先搭建起一个客户端框架。

#include <iostream>
#include <string>
#include <muduo/net/TcpClient.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>class DictClient
{
private:void onConnection(const muduo::net::TcpConnectionPtr& conn) {if (conn->connected()) {std::cout << "连接成功" << std::endl;_conn = conn;}else {std::cout << "连接断开" << std::endl;_conn.reset();}}void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf) {std::string res = buf->retrieveAllAsString();std::cout << res << std::endl;}public:DictClient(std::string serverip, uint16_t serverport):_client(&_baseloop, muduo::net::InetAddress(serverip, serverport), "DictClient"){_client.setConnectionCallback(std::bind(&DictClient::onConnection, this,std::placeholders::_1));_client.setMessageCallback(std::bind(&DictClient::onMessage, this,std::placeholders::_1,std::placeholders::_2)));_client.connect(); // 连接服务器_baseloop.loop();  // 开始事件循环监控}bool Send(std::string msg){if (!_conn->connected()) {std::cout << "连接断开" << std::endl;return false;}_client.send(msg);return true;}private:muduo::net::TcpConnectionPtr _conn;muduo::net::EventLoop _baseloop;muduo::net::TcpClient _client;
};

这段代码中有一个问题:构造函数中 _baseloop.loop() 开始执行后,主线程就进入了死循环,这会导致后面的一系列操作都无法被正常执行。因此,需要一个新的线程,单独负责事件循环监控

2.1 使用 EventLoopThread 管理独立的事件循环
class DictClient
{
public:DictClient(std::string serverip, uint16_t serverport): _baseloop(_loopthread.startLoop()) // 开始事件循环监控, _client(_baseloop, muduo::net::InetAddress(serverip, serverport), "DictClient"){// ..._client.connect();_loopthread.startLoop(); // 开启事件循环监控}
private:muduo::net::EventLoopThread _loopthread; // 新增muduo::net::EventLoop *_baseloop;muduo::net::TcpConnectionPtr _conn;muduo::net::TcpClient _client;
};
2.2 引入 CountDownLatch 类

CountDownLatch 是一个同步辅助类,用于确保某个操作在多线程中按预期顺序进行。

class DictClient
{
private:void onConnection(const muduo::net::TcpConnectionPtr& conn) {if (conn->connected()) {std::cout << "连接成功" << std::endl;_downlatch.countDown(); // 连接成功 -- 为 0,将主线程唤醒_conn = conn;}else {std::cout << "连接断开" << std::endl;_conn.reset();}}
public:DictClient(std::string serverip, uint16_t serverport): _downlatch(1) // 指定初始值为 1, _baseloop(_loopthread.start()), _client(_baseloop, muduo::net::InetAddress(serverip, serverport), "DictClient"){_client.setConnectionCallback(std::bind(&DictClient::onConnection, this,std::placeholders::_1));// ..._client.connect();_downlatch.wait(); // 将主线程阻塞,等待连接成功}
private:muduo::CountDownLatch _downlatch;muduo::net::EventLoopThread _loopthread; // 新增muduo::net::EventLoop *_baseloop;muduo::net::TcpConnectionPtr _conn;muduo::net::TcpClient _client;
};

工作流程:

  1. 初始化 CountDownLatchEventLoopThread

    • _downlatch 初始化为 1 ,表示主线程需要等待一次 countDown() —— 计数器减为 0

    • _loopthread 用于创建一个单独的线程,运行 EventLoop

  2. 设置 连接 / 消息 事件回调函数,连接到服务器,阻塞主线程

  3. 连接成功回调 onConnection()_downlatch.countDown()CountDownLatch 的计数器减为 0 ,唤醒阻塞的主线程

2.3 启动客户端
int main() {DictClient client("127.0.0.1", 8080);while (1) {std::string str;std::cin >> str;client.Send(str);}return 0;
}

http://www.mrgr.cn/news/68284.html

相关文章:

  • Flutter中文字体设置指南:打造个性化的应用体验
  • Hive面试题-- 查询各类型专利 top10 申请人及专利申请数
  • 基础网络安全知识
  • 鉴源实验室·加密技术在汽车系统中的应用
  • lsblk 命令学习
  • 动态规划理论基础和习题【力扣】【算法学习day.25】
  • 05-如何统一管理纷繁杂乱的数据指标?
  • Bsin-kb-agent:企业级AI知识库
  • 九泰智库 | 医械周刊- Vol.68
  • 第3篇 滑动开关控制LED__ARM汇编语言工程<一>
  • Springboot 整合 Java DL4J 打造自然语言处理之语音识别系统
  • aspose如何获取PPT放映页“切换”的“持续时间”值
  • 黑龙江二级等保与CDN的深度关联:加速安全,护航数字化转型
  • 单位正交矢量的参数化,用于特征矢量对厄尔米特矩阵对角化使用
  • 国产化浪潮下,高科技企业如何选择合适的国产ftp软件方案?
  • 系统架构设计师论文:模型驱动架构设计方法及其应用
  • 亲测高效!一款能简化PPT制作的AI工具笔格AIPPT
  • 如何让ffmpeg运行时从当前目录加载库,而不是从/lib64
  • 【go从零单排】接口(interface)和多态(Polymorphism)
  • 必须安装的 IDEA 插件,强烈推荐【安装及教程】
  • selenium大量并发连接驱动超时
  • synchronized锁的八种情况
  • uniapp的基本使用(easycom规范和条件编译)和uview组件的安装和使用
  • 数据挖掘实战-基于SARIMA时间序列模型预测Netflix股票未来趋势
  • 虚拟化数据恢复—XenServer虚拟机中SQL Server数据库数据恢复案例
  • 常用滤波算法(十一)-卡尔曼滤波