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)

|

|--- 处理读写事件

整体代码:

cpp 复制代码
/*
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;

}

头文件 & 命名空间

cpp 复制代码
#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() 在干什么?

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

为什么 EventLoop 不能拷贝?

内部持有 fd

绑定线程 ID

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

TcpServer

TcpServer 本质是什么?

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

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

TcpServer 的职责

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

InetAddress(地址封装)

cpp 复制代码
InetAddress addr("127.0.0.1", 6000);

等价于:

cpp 复制代码
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 构造函数

cpp 复制代码
ChatServer(EventLoop* loop,
           const InetAddress& listenAddr,
           const string& nameArg)
    : _server(loop, listenAddr, nameArg),
      _loop(loop)

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

回调机制

cpp 复制代码
_server.setConnectionCallback(
    bind(&ChatServer::onConnection, this, _1)
);

muduo 什么时候调用它?

✔ accept 成功

✔ TCP 连接建立

✔ TCP 连接断开

TcpConnectionPtr

cpp 复制代码
const TcpConnectionPtr& conn

等价于

cpp 复制代码
shared_ptr<TcpConnection>

为什么一定要 shared_ptr?

连接可能:

正在读

正在写

正在关闭

防止提前析构

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

这是 muduo 稳定性的核心设计

setMessageCallback

cpp 复制代码
_server.setMessageCallback(
    bind(&ChatServer::onMessage, this, _1, _2, _3)
);

什么时候触发?

socket fd EPOLLIN

onMessage 参数拆解

cpp 复制代码
const TcpConnectionPtr& conn
Buffer* buffer
Timestamp time

Buffer 是什么?

muduo 的 用户态缓冲区

socket → kernel buffer → Buffer → 用户

Timestamp

cpp 复制代码
time.toString()

= 消息到达服务器的时间

= muduo 在 epoll 返回时打的时间戳

send()

cpp 复制代码
conn->send(buf);

send 是怎么做到非阻塞的?

尝试 write

写不完 → 放入 output buffer

注册 EPOLLOUT

可写时继续写

main 函数执行全过程

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

相关推荐
想放学的刺客1 天前
整理了120道单片机嵌入式面试题与答案,覆盖了硬件电路和C语言等核心领域。
c语言·c++·stm32·单片机·嵌入式硬件·mcu·51单片机
星辰徐哥1 天前
C语言网络编程入门:socket编程、TCP/IP协议、客户端与服务器通信的实现
c语言·网络·tcp/ip
清漠2331 天前
win11“网络和Internet“中无“以太网“这个选项解决记录
服务器·网络·数据库
普通网友1 天前
多协议网络库设计
开发语言·c++·算法
努力努力再努力wz1 天前
【Linux网络系列】:TCP 的秩序与策略:揭秘传输层如何从不可靠的网络中构建绝对可靠的通信信道
java·linux·开发语言·数据结构·c++·python·算法
意疏1 天前
技术党必看:节点小宝网关模式上线,无需客户端享远程访问,附新春抽NAS奖攻略
网络·智能路由器·节点小宝
时空潮汐1 天前
神卓N600 NAS远程访问实用指南(两篇)
网络·神卓nas·神卓n600 pro·家庭轻nas
清清&1 天前
【网络】一文读懂HTTPS协议
网络·https·智能路由器
sg_knight1 天前
如何为 Claude Code 配置代理与网络环境
网络·ai·大模型·llm·claude·code·claude-code
汉克老师1 天前
GESP2024年3月认证C++二级( 第二部分判断题(1-10))
c++·循环结构·分支结构·gesp二级·gesp2级