前言
首先我对之前的目录做了一些优化,按照标准开源代码的形式(这个我在C++集群聊天服务器(2)中讲解过)进行了改善:

接下来开始讲解这些文件中的代码以及他们之间的联系。
一、按模块分开来讲解
这次主要实现了这六个文件的代码:
chatserver.h + chatserver.cpp → 网络模块(负责底层网络 IO)
chatservice.h + chatservice.cpp → 业务模块(负责具体聊天业务逻辑)
public.h → 公共协议文件(server 和 client 共享的消息类型定义)
main.cpp → 程序入口(启动服务器)
核心思想:网络模块只管"收发数据",业务模块只管"处理数据",通过 JSON + msgid 分发机制 实现完全解耦。
接下来也会以这六个文件进行重点讲解。
1.public.h------消息协议公共约定
这个文件的作用主要是:
- 定义了客户端和服务端通信的消息类型枚举(EnMsgType)。
- 当前只有两种:LOGIN_MSG = 1(登录)、REG_MSG = 2(注册)。
- 这是一个server 和 client 共用的头文件,确保双方对消息 ID 的理解一致。
- 实际项目中会扩展更多类型(如一对一聊天、群聊、添加好友等)。
那我们为什么需要设计这个公共文件:
它是整个消息系统的"协议规范",客户端发送 JSON 时必须带 "msgid" 字段,服务端据此识别业务类型。
public.h的代码非常简洁:
cpp
#ifndef PUBLIC_H
#define PUBLIC_H
/*
server和client的公共文件
*/
enum EnMsgType
{
LOGIN_MSG = 1, //登录消息
REG_MSG //注册消息
};
#endif
2. chatserver.h + chatserver.cpp ------ 网络模块
我们先分别来看看这两个文件的作用:
1.chatServer.h的作用:
- 声明聊天服务器主类。
- 成员:muduo 的 TcpServer 和 EventLoop*;
- 接口:构造函数、start();
- 回调:onConnection(连接建立/断开)、onMessage(收到消息)。
2.chatserver.cpp的作用:
- 实现构造函数:绑定 muduo 的连接/消息回调,设置 IO 线程数;
- start():启动 TcpServer 开始监听;
- onConnection:当前只处理客户端断开(调用 shutdown);
- onMessage:网络模块的核心逻辑:
1.从 Buffer 取出原始字符串。
2.用 nlohmann::json 解析成 JSON 对象。
3.取出 js["msgid"],调用 ChatService::instance()->getHandler(msgid) 获取业务处理器。
4.执行处理器:msgHandler(conn, js, time)。
他们只负责底层 TCP 连接管理、IO 事件处理、数据初步解析(转 JSON);不关心业务,代码里没有任何 login/reg 等业务词语。
ChatServer.h代码:
cpp
#ifndef CHATSERVER_H
#define CHATSERVER_H
#include <muduo/net/TcpServer.h>
#include<muduo/net/EventLoop.h>
using namespace muduo;
using namespace muduo::net;
//聊天服务器的主类
class ChatServer
{
public:
//初始聊天服务器对象
ChatServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg);
//启动服务
void start();
private:
//上报链接相关信息的回调函数
void onConnection(const TcpConnectionPtr&);
//上报读写事件相关信息的回调函数
void onMessage(const TcpConnectionPtr&,
Buffer *,
Timestamp);
TcpServer _server; //组合的muduo库,实现服务器功能的类对象
EventLoop *_loop; //指向事件循环对象的指针
};
#endif
chatserver.cpp代码:
cpp
#include"chatserver.hpp"
#include"json.hpp"
#include"chatservice.hpp"
#include<functional>
#include<string>
using namespace std;
using namespace placeholders;
using json = nlohmann::json;
ChatServer::ChatServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg)
:_server(loop,listenAddr,nameArg),_loop(loop)
{
//注册连接回调
_server.setConnectionCallback(std::bind(&ChatServer::onConnection,this,_1));
//注册消息回调
_server.setMessageCallback(std::bind(&ChatServer::onMessage,this,_1,_2,_3));
//设置线程数量
_server.setThreadNum(4);
}
//启动服务
void ChatServer::start()
{
_server.start();
}
//上报链接相关信息的回调函数
void ChatServer::onConnection(const TcpConnectionPtr &conn)
{
//客户端断开连接
if(!conn->connected())
{
conn->shutdown();
}
}
//上报读写事件相关信息的回调函数
void ChatServer::onMessage(const TcpConnectionPtr &conn,
Buffer *buffer,
Timestamp time)
{
string buf = buffer->retrieveAllAsString();
//数据的反序列化
json js = json::parse(buf);
//达到的目的:完全解耦网络模块的代码和业务模块的代码
//通过js["msgid"]获取->业务handler->conn js time
auto msgHandler = ChatService::instance()->getHandler(js["msgid"].get<int>());
//回调消息绑定好的事件处理器,来执行相应的业务处理
msgHandler(conn,js,time);
}
3. chatservice.h + chatservice.cpp------业务模块
我们仍然先分析内部函数的作用:
1.chatservice.h的作用:
- 声明业务主类(单例模式)。
- 定义 MsgHandler 类型:统一的业务回调函数签名。
- 接口:instance() 获取单例、getHandler(int msgid) 获取处理器。
- 示例业务:login()、reg()。
- 成员:_msgHandlerMap(msgid → 处理函数 的 map)。
2.chatservice.cpp的作用:
- 单例实现(静态局部变量,线程安全)。
- 构造函数:注册业务处理器(用 std::bind 绑定 login/reg)。
- getHandler:根据 msgid 返回对应函数;若不存在,返回空操作并打错误日志。
- login/reg:当前只是占位(打印日志),实际项目中会查数据库、验证密码、维护用户状态等。
这两个文件只负责具体业务逻辑;通过 map 实现消息分发:msgid → 具体处理函数;所有连接共享同一个单例实例,便于管理全局状态(用户表、在线列表等)。
chatservice.h的代码:
cpp
#ifndef CHATSERVICE_H
#define CHATSERVICE_H
#include<muduo/net/TcpConnection.h>
#include<unordered_map>
#include<functional>
#include<json.hpp>
using namespace std;
using namespace muduo;
using namespace muduo::net;
using json = nlohmann::json;
//表示处理消息的事件回调方法类型
using MsgHandler = std::function<void(const TcpConnectionPtr &conn,json &js,Timestamp)>;
//聊天服务器业务类
class ChatService
{
public:
//获取单例对象的接口函数
static ChatService* instance();
//处理登录业务
void login(const TcpConnectionPtr &conn,json &js,Timestamp time);
//处理注册业务
void reg(const TcpConnectionPtr &conn,json &js,Timestamp time);
//获取消息对应的处理器
MsgHandler getHandler(int msgid);
private:
ChatService();
//存储消息id和其对应的业务处理方法
unordered_map<int,MsgHandler> _msgHandlerMap;
};
#endif
chatservice.cpp的代码:
cpp
#include"chatservice.hpp"
#include"public.hpp"
#include<muduo/base/Logging.h>
using namespace muduo;
ChatService* ChatService::instance()
{
static ChatService service;
return &service;
}
//注册消息以及对应的Handler回调操作
ChatService::ChatService()
{
_msgHandlerMap.insert({LOGIN_MSG,std::bind(&ChatService::login,this,_1,_2,_3)});
_msgHandlerMap.insert({REG_MSG,std::bind(&ChatService::reg,this,_1,_2,_3)});
}
//获取消息对应的处理器
MsgHandler ChatService::getHandler(int msgid)
{
//记录错误日志,msgid没有对应的事件处理回调
auto it = _msgHandlerMap.find(msgid);
if(it == _msgHandlerMap.end())
{
//返回一个默认的处理器,空操作
return [=](const TcpConnectionPtr &conn,json &js,Timestamp)
{
LOG_ERROR<<"msgid:"<<msgid<<"can not find handler!";
};
}
else
{
return _msgHandlerMap[msgid];
}
}
//处理登录业务
void ChatService::login(const TcpConnectionPtr &conn,json &js,Timestamp time)
{
LOG_INFO<<"do login service!!!";
}
//处理注册业务
void ChatService::reg(const TcpConnectionPtr &conn,json &js,Timestamp time)
{
LOG_INFO<<"do reg service!!!";
}
4. main.cpp------程序入口
main.cpp的作用:
- 创建 EventLoop(muduo 事件循环);
- 设置监听地址(127.0.0.1:6000);
- 实例化 ChatServer 并启动;
- 调用 loop.loop() 进入无限事件循环,处理所有 IO。
这是典型 muduo 启动模板,非常的简洁。
main.cpp的代码:
cpp
#include"chatserver.hpp"
#include<iostream>
using namespace std;
int main()
{
EventLoop loop;
InetAddress addr("127.0.0.1",6000);
ChatServer server(&loop,addr,"ChatServer");
server.start();
loop.loop();
return 0;
}
二、整体联系
这些文件之间到底是如何来协作的呢?
以下是一个完整的消息处理流程:
-
启动流程(main.cpp → chatserver → muduo):
main 创建 EventLoop 和 ChatServer;
ChatServer 构造函数注册 onConnection/onMessage 回调;
start() + loop.loop() 开始监听和事件处理。
-
连接建立:
客户端连接 → muduo 调用 onConnection(网络模块处理上线/下线)。
-
消息处理链路(核心解耦点):
客户端发送 JSON(如 {"msgid":1, "id":1001, "password":"123456"});
然后muduo 收到数据 → 调用 ChatServer::onMessage(网络模块);
网络模块:先解析 JSON,再通过 msgid 从ChatService单例获取 handler(调用 getHandler);
再执行 handler → 进入业务模块(如 login);
业务模块处理后,最后通过 conn->send(...) 发送响应 JSON 给客户端;
关键桥梁:public.h 的 msgid(协议约定);ChatService 的 handler map(分发机制);TcpConnectionPtr(muduo 提供的连接对象,用于发送响应)。
这里运用单例模式的作用是所有连接的业务处理都通过同一个 ChatService 实例,便于共享状态。
数据流向图解:
text
客户端 → TCP → muduo → ChatServer::onMessage → JSON解析 → ChatService::getHandler → 业务函数(login/reg) → conn->send(响应) → 客户端
三、这种实现的好处
-
完全解耦(最大优点):
网络模块和业务模块零耦合:改业务不需要动网络代码,反之亦然;
网络代码简洁,只关心 IO;业务代码专注逻辑。
-
高可维护性和可扩展性:
加新业务(如群聊)只需三步:public.h 加新 msgid;ChatService 构造函数注册新 handler;实现新业务函数。
网络模块一行不用改!
-
适合集群扩展:
当前是单机,但设计已为集群铺路:
ChatService 单例便于注入 Redis(分布式存储用户状态、消息路由);
连接无状态:所有用户状态集中在业务层(后续放 Redis),多台服务器可共享;
支持负载均衡(Nginx 或其他)+ Redis Pub/Sub 实现消息跨服务器广播。
-
高性能:
muduo 是高性能 reactor 库,支持多线程 IO;
JSON 解析轻量,handler map 查询 O(1)。
-
健壮性好:
msgid 不存在时有默认空处理 + 日志;
以及单例线程安全。