C++项目 | 集群聊天服务器 | 业务模块

要达到的目的:完全解耦网络模块的代码和业务模块的代码

通过js[“msgid”]绑定一个回调函数,对应获得一个业务处理器handler(handler就是回调函数)

只要解析出来msgid就可以回调对应的函数,而不用写什么if(msgid==1)就怎么样怎么样这样的代码

1.头文件

使用单例模式实现聊天服务器业务类

用函数对象表示回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef PUBLIC_H
#define PUBLIC_H

/*
server和client的公共文件
*/

enum EnMsgType
{
LOGIN_MSG=1, //登录消息
REG_MSG //注册消息
};


#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//chatservice.hpp
#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

2.具体实现的cpp文件

用Map来存储msgid和msgid对应的回调函数(handler)

我们解析出来是哪个msgid就可以直接返回对应的函数对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#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!!!";
}

3.测试文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//main.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;
}

4.ChatServer中的改动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include"chatserver.hpp"
#include"json.hpp"
#include"chatservice.hpp"

#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())
{
//释放sockfd资源
conn->shutdown();
}
}

//上报读写时间相关信息的回调函数
void ChatServer::onMessage(const TcpConnectionPtr& conn,
Buffer* buffer,
Timestamp time)
{
//把Buffer里面存着的放到字符串里面去
string buf=buffer->retrieveAllAsString();
//数据的反序列化
json js=json::parse(buf);

//通过js["msgid"]绑定一个回调函数,获得一个业务处理器handler
//只要解析出来msgid就可以回调对应的函数
//要达到的目的:完全解耦网络模块的代码和业务模块的代码
auto msgHandler=ChatService::instance()->getHandler(js["msgid"].get<int>());
//回调消息绑定好的事件处理器,来执行相应的业务处理
msgHandler(conn,js,time);
}

可以看到在最后的onMessage中,只需要

1
2
auto msgHandler=ChatService::instance()->getHandler(js["msgid"].get<int>());
msgHandler(conn,js,time);

这两行代码,就可以完成对应的回调函数(处理器handler)的调用了

而不需要在网络模块写什么类似于这样的代码,需要调用到业务模块的具体函数,这样写网络模块和业务模块耦合度就很高了,对修改(比如增删改业务)很不方便

1
2
3
4
5
if(msgid==1)
{
login(...);
....
}

5.测试结果

image-20250118173358144

可以看到,Chatserver可以对应的返回登录信息,注册信息,并且没有的业务也有报错。

6.这次过程中要在顶级CMake中的头文件路径中加入thirdparty

1
include_directories(${PROJECT_SOURCE_DIR}/thirdparty)