news 2026/4/16 15:46:40

C++-集群聊天室(2):muduo网络库

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++-集群聊天室(2):muduo网络库

用 muduo 写一个 TCP 服务器

基于epoll + 线程池

接收客户端连接

客户端发什么,服务器原样返回(Echo)

打印连接、断开、收发数据日志

muduo 的核心思想

网络 I/O 与业务逻辑彻底解耦

muduo 的核心模型

muduo =Reactor + One Loop Per Thread

整体结构分析

main()
└── EventLoop loop // 主 Reactor(epoll)
└── ChatServer server
└── TcpServer _server
├── ConnectionCallback
├── MessageCallback
└── 线程池 (setThreadNum)

典型 muduo Reactor 模型

主线程(EventLoop)
|
|--- 接收新连接
|--- 分发连接到子线程(EventLoop)
|
|--- 处理读写事件

整体代码:

/* muduo网络库给用户提供两个主要的类 TcpServer:用户编写服务器程序的 TcpClient:用户编写客户端程序的 epoll + 线程池 好处:能够把网络I/O的代码和业务代码区分开来 用户的连接与断开 用户的可读写事件 */ #include<muduo/net/TcpServer.h> #include<muduo/net/EventLoop.h> #include<iostream> #include<functional> using namespace std; using namespace muduo; using namespace muduo::net; using namespace placeholders; /*基于muduo网络库开发服务器程序 1.组合TcpSever对象 2.创建EventLoop事件循环对象的指针 3.明确TcpServer构造函数需要什么参数,输出ChatServer的构造函数 */ class ChatServer { public: ChatServer(EventLoop * loop,const InetAddress &listenAddr,const string &nameArg):_server(loop,listenAddr,nameArg),_loop(loop) { _server.setConnectionCallback(bind(&ChatServer::onConnection,this,_1)); _server.setMessageCallback(bind(&ChatServer::onMessage,this,_1,_2,_3)); //设置EventLoop的线程个数 _server.setThreadNum(10); } //启动ChatServer服务 void start() { _server.start(); } private: //TcpServer绑定的回调函数,当有新连接或连接中断时调用 void onConnection(const TcpConnectionPtr &conn) { if(conn->connected()) { 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(); } } //TcpServer绑定的回调函数,当有新数据时调用 void onMessage(const TcpConnectionPtr& conn, Buffer* buffer, Timestamp time) { string buf=buffer->retrieveAllAsString(); cout<<"recv data:"<<buf<<"time:"<<time.toString()<<endl; conn->send(buf); } TcpServer _server; EventLoop* _loop; }; int main() { EventLoop loop;//epoll InetAddress addr("127.0.0.1",6000); ChatServer server(&loop,addr,"ChatServer"); server.start();//listen epoll_ctl=>epoll loop.loop();//epoll_wait以阻塞方式等待新用户连接,已连接用户的读写事件等 return 0; }

头文件 & 命名空间

#include <muduo/net/TcpServer.h> #include <muduo/net/EventLoop.h> #include <functional> using namespace muduo; using namespace muduo::net; using namespace placeholders;

EventLoop(Reactor 的本体)

EventLoop loop;

EventLoop 是什么?

EventLoop = epoll + 事件分发器

loop.loop() 在干什么?

void EventLoop::loop() { while (!quit_) { activeChannels_ = poller_->poll(); // epoll_wait for (Channel* ch : activeChannels_) { ch->handleEvent(); } } }

为什么 EventLoop 不能拷贝?

内部持有 fd

绑定线程 ID

一个 loop 只能在一个线程里跑

TcpServer

TcpServer 本质是什么?

TcpServer = Acceptor + 线程池 + 连接管理器

class TcpServer { EventLoop* loop_; // 主 loop Acceptor acceptor_; // 监听 socket EventLoopThreadPool threadPool_; map<string, TcpConnectionPtr> connections_; };

TcpServer 的职责

功能谁干
accept 新连接Acceptor
分配 IO 线程ThreadPool
管理连接connections_
注册回调setXXXCallback

InetAddress(地址封装)

InetAddress addr("127.0.0.1", 6000);

等价于:

sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(6000); addr.sin_addr.s_addr = inet_addr("127.0.0.1");

ChatServer 构造函数

ChatServer(EventLoop* loop, const InetAddress& listenAddr, const string& nameArg) : _server(loop, listenAddr, nameArg), _loop(loop)

创建 TcpServer
内部创建监听 socket
但 还没 listen

回调机制

_server.setConnectionCallback( bind(&ChatServer::onConnection, this, _1) );

muduo 什么时候调用它?

✔ accept 成功
✔ TCP 连接建立
✔ TCP 连接断开

TcpConnectionPtr

const TcpConnectionPtr& conn

等价于

shared_ptr<TcpConnection>

为什么一定要 shared_ptr?

连接可能:

正在读

正在写

正在关闭

防止提前析构

保证回调执行期间对象存在

这是 muduo 稳定性的核心设计

setMessageCallback

_server.setMessageCallback( bind(&ChatServer::onMessage, this, _1, _2, _3) );

什么时候触发?

socket fd EPOLLIN

onMessage 参数拆解

const TcpConnectionPtr& conn Buffer* buffer Timestamp time

Buffer 是什么?

muduo 的用户态缓冲区

socket → kernel buffer → Buffer → 用户
Timestamp

time.toString()

= 消息到达服务器的时间
= muduo 在 epoll 返回时打的时间戳

send()

conn->send(buf);

send 是怎么做到非阻塞的?

尝试 write
写不完 → 放入 output buffer
注册 EPOLLOUT
可写时继续写


main 函数执行全过程

main
EventLoop loop
ChatServer server
server.start()
├ socket()
├ bind()
├ listen()
├ 创建线程池
loop.loop()
└ epoll_wait (阻塞)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 14:00:30

信号处理仿真:语音信号处理_(10).回声消除技术

回声消除技术 1. 回声的产生和分类 在语音通信系统中&#xff0c;回声是一个常见的问题&#xff0c;它会影响通话质量&#xff0c;甚至导致通话中断。回声的产生可以分为两种主要类型&#xff1a;声学回声和电学回声。 1.1 声学回声 声学回声是由于扬声器发出的声音被周围的…

作者头像 李华
网站建设 2026/4/16 12:35:59

[LCD Monitor]如何超频至540Hz

文章目录 [LCD Monitor]如何超频至540Hz 如何调整屏幕的分辨率与刷新频率 [LCD Monitor]如何超频至540Hz 最新版的Windows 11 build现在已经可以在微软官网看到更新信息, 请 至这里来下载最新更新并解锁PG248QP 屏幕的540 Hz 更新率支持. (如果你是Windows 10 系统的用户, 也必…

作者头像 李华
网站建设 2026/4/15 18:18:27

Linux命令-login(登录系统)

&#x1f9ed;说明 login 命令是Linux系统中用于登录系统的核心工具&#xff0c;它负责验证用户身份并为用户开启一个新的会话。下面这个表格汇总了它的基本用法。项目说明基本作用登录系统&#xff0c;启动新的用户会话。命令格式login [选项] [用户名]常用选项-p: 保持当前环…

作者头像 李华
网站建设 2026/4/16 10:59:48

设计模式组合应用:智能硬件控制系统

作为嵌入式工程师&#xff0c;你大概率遇到过这类棘手场景&#xff1a;智能硬件的控制逻辑越堆越臃肿&#xff0c;状态切换混乱不堪&#xff0c;模块间耦合得像一团乱麻&#xff0c;新增一个小功能就要动好几处核心代码&#xff0c;调试时对着几百行嵌套代码根本找不到问题根源…

作者头像 李华
网站建设 2026/4/16 10:55:29

干货分享|谁在打破虚拟与现实的次元壁?

具身智能与非具身智能在本质上的显著差异&#xff0c;集中体现在物理具现化能力的实现上&#xff0c;这一差异主要从感知与行动的闭环机制&#xff0c;物理规律的内化理解&#xff0c;仿真到现实的迁移能力与知识获取方式四个维度得以体现。 1 1. 感知与行动的闭环机制 以特…

作者头像 李华
网站建设 2026/4/16 12:46:05

Cursor-memory-cli 自动化记忆提取的完整实现

Cursor-memory-cli 自动化记忆提取的完整实现 一个用于安装和配置 Cursor Memory 系统的命令行工具。Cursor Memory 是一个持续学习系统&#xff0c;可以从 Cursor 会话中提取结构化记忆&#xff0c;并将其编码为可查询的 JSON 记录&#xff0c;供跨会话调用。 基于 Cursor Me…

作者头像 李华