muduo库的初步认识和基本使用,创建一个简单查询单词服务系统

article/2025/7/18 10:44:43

小编在学习完muduo库之后,觉得对于初学者,muduo库还是有点不好理解,所以在此,小编来告诉大家muduo库的初步认识和基本使用,让初学者也可以更快的上手和使用muduo库

Muduo由陈硕大佬开发,是⼀个基于 非阻塞IO和事件驱动的C++高并发TCP网络编程库 。 它是⼀款基
于主从Reactor模型的网络库,其使用的线程模型是one loop per thread, 所谓one loop per thread
指的是:
        a. ⼀个线程只能有⼀个事件循环(EventLoop), 用于响应计时器和IO事件
        b.⼀个文件描述符只能由⼀个线程进行读写,换句话说就是⼀个TCP连接必须归属于某个EventLoop管理。

一、muduo的初步认识

1、对TcpServer.h的初步认识

这张图片告诉大家muduo库的构造函数,让我来解释一下没给参数的概念及意思。

        a.EventLoop* loop 是TcpServer的事件循环监控,它是一个死循环,大家知道多路转接的时候需要一个事件循环监控,来告诉我们那个事件已经就绪,然后我们来进行对这个事件的操作,所以这个loop就是来做这个事情的,后面我们会介绍这个 loop的头文件,不着急。

        b.const InetAddress& listenAddr 储存的是TcpServer监听的ip地址和端口号,TcpServer要进行启动,必须有一个可以被外部连接的ip和端口号才可以。

        c.const string& nameArg 是你这个TcpServer服务的名字,这个就是自己对自己的服务起一个名字。

        d.Option option = kNoReusePort 是一个选项,告诉操作系统是否要开启地址复用,他的选项 第一个KNoReusePort是不开启地址复用,第二个KReusePort是开启地址复用。

2、对TcpServer.h中其他重要函数的认识

这个函数是TcpServer.h中必须要进行设置的函数,这个函数的意思是我们接收到外部的连接后需要调用的回调函数,可能到这里大家还不知道有啥用,先不着急,继续往后看。

这个函数也是TcpServer.h中必须要设置的函数,这个函数的意思是我们进行事件监控之后,收到外部发来的消息需要对消息进行的处理回调函数,也是继续往后看。

这个函数是开启TcpServer服务

3、对EventLoop.h的认识

构造函数没有参数,直接无参构造就好。

这个函数来直接启动事件循环操作,内部是一个死循环

4、对TcpClient.h的认识

还是一样,让我来告诉大家这个TcpClient的构造函数的参数意思。

        a.EventLoop* loop 和TcpServer构造函数中的意思一样,但是有个不一样的地方是在TcpClient中不能让TcpClient是一个死循环,因为EventLoop是一个死循环,因为客户端要进行对服务端发送消息,不能让事件循环监控是一个死循环这样客户端就不能往下执行了,只能一直对事件进行循环监控,后面使用一下大家也就会了。

        b. const InetAddress& serverAddr 的意思客户端要访问的服务端的ip和端口号。

        c.const string& nameArg 的意思和服务端的意思一样,也就是给客户端起一个名字。

5、对TcpClient.h中重要的函数的认识

这个函数的意思是对客户端连接服务端之后调用的回调函数,这个函数也必须要设置

这个函数的意思是客户端进行事件监控之后,收到消息之后,调用的对消息处理的回调函数,也必须要设置

这个函数的意思是对服务端进行连接,连接之后直接自己启动客户端的操作。

6、对TcpConnection.h的认识

这个类比较特别,不需要我们对它进行手动设置参数,它是我们客户端连接到服务端之后,服务端和客户端会创建一个TcpConnection来保存客户端和服务端的信息,包括客户端和服务端的端口号和ip地址,为两端进行通信创建条件。

这个send的接口就是我们进行通信的基础,虽然这个函数有三个重载函数,但是他们的效果都是一样的,都是向对方发送信息报文

这两个接口告诉我们两端是否连接成功,connected接口告诉我们两端是否连接正常,disconnected反之

7、对Buffer.h的认识

这个类也就是muduo库自己设置的缓冲区,方便我们对双方的消息进行处理和保存的类,这个类也不需要我们自己进行设置,他自己会进行设置,所以这里的构造函数的参数不需要我们自己管理,在这里我也就不说啦。

8、对Buffer.h中重要函数的认识

这些函数接口都是对Buffer缓冲区进行的读取数据的操作retrieve接口虽然有很多,但是他们的操作方式都一样,这里咱们以个别为例,retrieveInt64() 这个接口要读取64字节的数据,并且读取完成之后,会把这64字节的数据清空,很好理解,就是读取走缓冲区中的数据,其他的也都一样。还有retrieveAllAsString() 这个接口是用来读取Buffer缓冲区中的所有数据并把它当作一个字符串,也很好理解吧。其他的也都类似,这里就不做讲解了。

9、对EventLoopThread.h的认识

这个类和EventLoop的类功能相似,都是对事件进行循环监控,但是这个类是用开辟一个线程帮我们进行循环监控,这样我们主线程就可以做自己想做的事情了,也就是我们客户端需要用到的这个事件循环监控,因为客户端不可以被阻塞到事件循环监控那块。这个构造函数里面的参数也不用我们用户自己传,所以在这里我也不再讲解。

10、对EventLoopThread.h中重要函数的理解

这个函数来告诉我们它的EventLoop的指针,因为它的底层也是事件循环监控EventLoop。

11、对CountDownLatch.h的认识

这个类协助我们对客户端正确性进行维护,因为客户端在进行连接之后就会自动开始对服务端发送报文,但是如果客户端没有连接到服务端就对服务端发送消息,这不就出错了吗,所以在这里才有了这个类,这个构造函数传的count就是一个数字,当这个数字减为0就唤醒阻塞,如果这个数字大于0就一直阻塞住,不让客户端发送消息给服务端,可能有点不理解,后面看例子一下子就明白。

12、对CountDownLatch.h中重要函数的理解

这个wait函数一直进行阻塞countDown函数每次调用就对构造函数中的count进行-1操作,直到减为0,就唤醒wait操作,还是和上面一样,一看例子就明白。

二、基本使用muduo库,创建一个简单的单词查询系统

1、首先我们先看服务端的实现及其代码和注释来理解

class dictServer
{
public:dictServer():_dictserver(&_loop, muduo::net::InetAddress("0.0.0.0", 9090),"dictserver", muduo::net::TcpServer::Option::kReusePort)  // 最后一个参数的意思是是否开启地址复用{// 设置这几个必须要的函数_dictserver.setConnectionCallback(std::bind(&dictServer::connectionCallBack, this, std::placeholders::_1));_dictserver.setMessageCallback(std::bind(&dictServer::messageCallBack, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 给字典插入数据 随便插入几个单词进行test实验_dictmap["hello"] = "你好";_dictmap["world"] = "世界";_dictmap["apple"] = "苹果";}void running(){// 启动tcp服务和事件循环监控loop服务_dictserver.start();_loop.loop();}~dictServer(){}
private:void connectionCallBack(const muduo::net::TcpConnectionPtr& conn){// 判断是否连接成功if(conn->connected()){std::cout << "连接成功!" << std::endl;}else{std::cout << "连接失败!" << std::endl;}}void messageCallBack(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp time){// 取出来buf里面的数据 进行查询 因为我们没有定制其他协议 这里如果收到客户端发来的一个单词直接全部取出来进行操作std::string str = buf->retrieveAllAsString();// 对这个单词进行查询auto it = _dictmap.find(str);std::string msg;if(it != _dictmap.end()){// 找到了数据msg = (*it).second;}else{msg = "None";}// 把查询到的数据发送给客户端conn->send(msg);}
private:// 因为需要现有 eventloop对象才可以初始化tcpserver对象 所以eventloop对象应在tcpserver上面 这样初始化列表才会正确muduo::net::EventLoop _loop;muduo::net::TcpServer _dictserver;std::unordered_map<std::string, std::string> _dictmap;
};int main()
{dictServer dict;// 启动服务端dict.running();return 0;
}

2、我们来看客户端的实现及其代码和注释来理解

class dictclient
{
public:dictclient()// 这里直接获取EventThreadLoop的指针给EventLoop对象:_loop(_loopthread.startLoop())     // loophtread 开始创建了对象之后 就可以自己在线程中自己循环监控数据, _client(_loop, muduo::net::InetAddress("127.0.0.1", 9090), "dictclient"),_downlatch(1)   // 这里的count我们设置为一, 如果连接上就给count--,直接唤醒wait操作,让客户端来进行通信{// 设置这两个必须设置的函数对象_client.setConnectionCallback(std::bind(&dictclient::connCallBack, this, std::placeholders::_1));_client.setMessageCallback(std::bind(&dictclient::mesCallBack, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_client.connect();  // 这里只是发起了客户端连接服务端的操作_downlatch.wait();  // 用CountDownLatch的类来对客户端进行保护阻塞操作}bool send(const std::string& mes){if(_conn->connected()){// 连接建立成功 可以发送消息_conn->send(mes);return true;}return false;}~dictclient() {}
private:void connCallBack(const muduo::net::TcpConnectionPtr &conn){if (conn->connected()){std::cout << "连接成功!" << std::endl;_conn = conn;     // 把这个conn给到自己_downlatch.countDown();  // 让计数器减减 唤醒阻塞client}else{std::cout << "连接失败!" << std::endl;_conn.reset();   // 释放这个指针指向的空间}}void mesCallBack(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp timestamp){// 取出来buf里面的数据 进行查询std::string str = buf->retrieveAllAsString();std::cout << str << std::endl;}private:    muduo::net::TcpConnectionPtr _conn;  // 因为客户端无法自己访问连接完毕之后创建的TcpConnection对象,所以在此我们进行手动设置一个这个对象muduo::CountDownLatch _downlatch;muduo::net::EventLoopThread _loopthread;muduo::net::EventLoop* _loop;muduo::net::TcpClient _client;
};int main()
{dictclient client;while(true){std::string mes;std::cin >> mes;client.send(mes);}return 0;
}

以上就是我们今天学习的所有内容,如果有什么不理解的地方,大家可以评论区留言或者私信我,我给大家答疑!下期再见啦!


http://www.hkcw.cn/article/QbTREeljbF.shtml

相关文章

格恩朗超声波水表 助力农业精准灌溉与振兴​

在农业现代化的征程中&#xff0c;水资源的精准利用至关重要&#xff0c;而这离不开高精度计量设备的支持。大连格恩朗品牌积极响应国家全面推进乡村振兴、加快农业农村现代化的号召&#xff0c;精心打造的超声波水表&#xff0c;凭借其超高精度&#xff0c;成为绿色灌溉领域的…

Nginx进阶篇(静态资源的缓存处理、Nginx中与浏览器缓存相关的指令、Nginx的跨域问题、静态资源防盗链)

文章目录 1. 静态资源的缓存处理1.1 什么是缓存1.2 什么是Web缓存1.3 Web缓存的种类1.3.1 客户端缓存1.3.2 服务端缓存 1.4 为什么要用浏览器缓存1.5 浏览器缓存的执行流程1.6 浏览器强缓存和弱缓存的区别1.6.1 强缓存&#xff08;Strong Cache&#xff09;1.6.2 弱缓存&#x…

云游戏混合架构

云游戏混合架构通过整合本地计算资源与云端能力&#xff0c;形成了灵活且高性能的技术体系&#xff0c;其核心架构及技术特征可概括如下&#xff1a; 一、混合架构的典型模式 分层混合模式‌ 前端应用部署于公有云&#xff08;如渲染流化服务&#xff09;&#xff0c;后端逻辑…

Origin教程010:Origin绘制同心圆图

文章目录 10、绘制同心圆弧图1、同心圆弧图绘制2、调整绘图顺序3、设置标签内容、样式4、指引线设置5、添加图形标题6、练习数据10、绘制同心圆弧图 本节要点: 同心圆弧图的绘制调整绘图顺序(对象管理器)设置标签内容、样式指引线设置添加图形标题1、同心圆弧图绘制 1️⃣拖…

rs485/232转profinet网关与长陆-UNI800称重显示控制仪通讯

rs485/232转profinet网关与长陆-UNI800称重显示控制仪通讯 在现代工业自动化系统中&#xff0c;RS485转Profinet网关作为一种关键的通信接口设备&#xff0c;其重要性不言而喻。它能够将传统的RS485接口设备接入先进的Profinet网络&#xff0c;实现不同协议之间的无缝转换和数…

【速通RAG实战:进阶】20、改进RAG检索质量有哪些诀窍?

一、数据层优化:构建高质量检索基础 (一)动态语义分块技术 传统固定长度分块易切断完整语义,采用基于相似度的动态分块策略可显著提升上下文连贯性。通过LangChain的SemanticChunker实现语义边界检测,当相邻文本相似度低于0.4时自动切分,避免将“设备型号-参数-操作步骤…

Nginx网站服务:从入门到LNMP架构实战

&#x1f3e1;作者主页&#xff1a;点击&#xff01; Nginx-从零开始的服务器之旅专栏&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年5月30日14点22分 前言 说起Web服务器&#xff0c…

早发现=早安心!超导心磁图如何捕捉早期病变信号?

随着生活节奏的加快&#xff0c;心血管疾病已成为威胁人们健康的“隐形杀手”。据国家心血管病中心发布的《中国心血管健康与疾病报告2022》显示&#xff0c;我国心血管病现患者人数已高达3.3亿&#xff0c;每5例死亡中就有2例死于心血管病。这一数据触目惊心&#xff0c;提醒我…

AI感知与行动:考拉悠然发布空间智能世界模型,让AI走进物理世界

本文转自&#xff1a;《封面新闻》 5月&#xff0c;2025福布斯中国人工智能科技企业TOP50评选结果发布&#xff0c;成都考拉悠然科技有限公司成功入选&#xff0c;成为榜单中唯一专注“空间智能”的企业。 &#xff0c;时长02:55 而在近日&#xff0c;考拉悠然发布了面向空间…

Arduino学习-跑马灯

1、效果 2、代码 /**** 2025-5-30 跑马灯的小程序 */ //时间间隔 int intervaltime200; //初始化函数 void setup() {// put your setup code here, to run once://设置第3-第7个引脚为输出模式for(int i3;i<8;i){pinMode(i,OUTPUT);} }//循环执行 void loop() {// put you…

CAD多边形密堆积2D插件

插件介绍 CAD多边形密堆积2D插件可在AutoCAD内建立模拟重力堆积状态的随机多边形颗粒及界面过渡区&#xff08;ITZ&#xff09;模型。 模型可分为多边形颗粒、界面过渡区&#xff08;ITZ&#xff09;、长方形试件三部分&#xff0c;各部分在CAD内分图层绘制&#xff0c;可批…

特伦斯 S75:重塑钢琴体验的数码钢琴之选

当传统钢琴的典雅质感与现代科技的精准赋能相遇&#xff0c;特伦斯 S75 立式数码钢琴应运而生。这款专为追求品质的演奏者与音乐爱好者设计的高端乐器&#xff0c;以 “还原三角钢琴灵魂&#xff0c;革新数字钢琴体验” 为核心理念&#xff0c;在音色、触感、音质与智能交互间达…

methods的实现原理

一、直观的感受methods的使用 首先直观的感受methods方法的使用&#xff0c;同样以计数器为例&#xff0c; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widt…

晨控CK-FR03与TwinCAT3配置EtherCAT通讯连接手册

晨控CK-FR03与TwinCAT3配置EtherCAT通讯连接手册 晨控CK-FR03系列作为晨控智能工业级别RFID读写器,支持大部分工业协议如RS232、RS485、以太网。支持工业协议Modbus RTU、Modbus TCP、Profinet、EtherNet/lP、EtherCat以及自由协议TCP/IP等。 本期主题&#xff1a;围绕CK-FR03…

大模型-attention汇总解析之-MLA

一、核心思想 先看下初始的MLA的一般性公式&#xff1a; 我们一般会缓存的是投影后的k_i, v_i而不是投影前的x_i, c_i &#xff0c;根据 MLA 的这个做法&#xff0c;通过不同的投影矩阵再次让所有的 K、V Head 都变得各不相同&#xff0c;那么 KV Cache 的大小就恢复成跟 MHA …

多线程(3)

1volatile关键字: 1.1volatile的功能 volatile关键字能够保证内存可见性 当变量被volatile修饰后: 写操作--->会将寄存器内的值修改后会第一时间将新值写回内存(主内存),不会引起一个另外一个线程去读的时候还读个旧数据,导致出现bug,比如将01改为1后就应该立马写回内存…

WIN11+VSCODE搭建c/c++开发环境

搭建c/cby win11vscode 前面试过了ubuntuvscode,macosvscode,win11visual studio搭建&#xff0c;本来以为win11vscode是手到擒来的&#xff0c;没想到颇有些周折。可能解决方案也并不完美&#xff0c;先记录下来&#xff0c;以后有改进再来修改。 安装vscode https://code.v…

20250530-C#知识:String与StringBuilder

String与StringBuilder string字符串在开发中经常被用到&#xff0c;不过在需要频繁对字符串进行增加和删除时&#xff0c;使用StringBuilder有利于提升效率。 1、String string是一种引用类型而非值类型&#xff08;某些方面像值类型&#xff09;使用“”进行两个string对象的…

04-redis-分布式锁-edisson

1 基本概念 百度百科&#xff1a;控制分布式系统之间同步访问共享资源方式。 在分布式系统中&#xff0c;常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源&#xff0c;那么访问这些资源的时候&#xff0c;往往需要互斥来防止…

01 Netty简介

文章目录 概要1 Hello World1.1 什么是netty&#xff1f;1.2 Netty 的核心特性1.3 初识 netty 2 Netty 的核心组件2.1 EventLoop 和 EventLoopGroup2.1.1 基本概念2.1.2 与 Channel 关联2.1.3 EventLoopGroup 的实现2.1.4 常用方法 2.2 Channel2.2.1 Channel 的类型2.2.2 Chann…