C++集群聊天服务器 网络模块+业务模块+CMake构建项目 笔记 (上)

跟着施磊老师做C++项目,施磊老师_腾讯课堂 (qq.com)

一、网络模块ChatServer

  • chatserver.hpp
#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& conn);// 上报读写事件相关信息的回调函数void onMessage(const TcpConnectionPtr& conn,Buffer* buffer,Timestamp time);TcpServer m_server; // 组合的muduo库,实现服务器功能的类对象EventLoop *m_loop;  // 指向事件循环的指针
};#endif
  • chatserver.cpp
#include "chatserver.hpp"
#include "chatservice.hpp"
#include "json.hpp"
#include <functional>
#include <string>
#include <iostream>
using namespace std;
using namespace placeholders;
using json = nlohmann::json;ChatServer::ChatServer(EventLoop *loop, const InetAddress &listenAddr, const string &nameArg): m_server(loop, listenAddr, nameArg), m_loop(loop) {// 注册链接回调m_server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));// 注册消息回调m_server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));// 设置线程数量m_server.setThreadNum(4);
}// 启动服务
void ChatServer::start() {m_server.start();
}// 上报链接相关信息的回调函数
void ChatServer::onConnection(const TcpConnectionPtr &conn) {// 客户端断开连接if(!conn->connected()) {conn->shutdown();// 释放socket fd资源}
}// 上报读写事件相关信息的回调函数
void ChatServer::onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time) {string buf = buffer->retrieveAllAsString();std::cout<<"buf: "<<buf.c_str()<<std::endl;// 数据的反序列化json js = json::parse(buf);// 达到的目的:完全解耦网络模块的代码和业务模块的代码// 通过js["msgid"] 获取 => 业务handler => conn js timeauto msghandler = ChatService::getInstance()->getHandler(js["msgid"].get<int>());// 回调消息绑定好的事件处理器,来执行相应的业务处理msghandler(conn,js,time);
}

json里边会包含一个msgid.由于客户端和服务器通信收发消息,需要判断这个消息是属于哪种业务的,就需要一个业务的标识,所以就用msgid来表示业务的标识.在onMessage函数中,并不想出现。

当有登录业务需求就调用相应的服务登录方法,当有注册业务需求就调用相应的服务注册方法,这样就用到if...else,或者switch case,但这种方式是直接调用服务层的方法,就把网络模块的代码和业务模块的代码给强耦合一起了,这不是好的方法.

方法二:每一个消息都有一个msgid(一个消息id映射一个事件处理),事先给它绑定一个回调操作,让一个id对应一个操作.不管具体做什么业务,并不会直接调用业务模块的相关的方法.

利用OOP回调思想,要想解耦模块之间的关系,一般有两种方法,一种就是使用基于面向接口的编程,在C++里边的"接口"可以理解为抽象基类.那也就是面向抽象基类的编程.另一种就是基于回调函数

这里使用基于回调函数来实现,m_msgHandlerMap存储消息id和其对应的业务处理方法.注册消息以及对应的Handler回调操作,就是把消息id对应的事件处理器给绑定了,LOGIN_MSG绑定的是login处理登录业务,REG_MSG绑定的是reg处理注册业务.

ChatService单例对象通过js["msgid"] 获取消息对应的处理器(业务handler)msghandler,由于回调消息绑定了事件处理器,可用它来执行相应的业务处理-->msghandler(conn,js,time);

二、业务模块ChatService

  • public.hpp
#ifndef PUBLIC_H
#define PUBLIC_H
/*server和client的公共文件
*/
enum EnMsgType {LOGIN_MSG = 1, // 登录消息REG_MSG // 注册消息
};
#endif // PUBLIC_H
  • chatservice.hpp
#ifndef CHATSERVICE_H
#define CHATSERVICE_H#include <muduo/net/TcpConnection.h>
#include <unordered_map>
#include <functional>
using namespace std;
using namespace muduo;
using namespace muduo::net;#include "json.hpp"
using json = nlohmann::json;// 表示处理消息的事件回调方法类型
using MsgHandler = std::function<void(const TcpConnectionPtr& conn,json& js,Timestamp)>;// 聊天服务器业务类
class ChatService {
public:// 获取单例对象的接口函数static ChatService* getInstance();// 处理登录业务void login(const TcpConnectionPtr& conn,json& js,Timestamp time); // 处理注册业务(register)void reg(const TcpConnectionPtr& conn,json& js,Timestamp time); // 获取消息对应的处理器MsgHandler getHandler(int msgid);ChatService(const ChatService&) = delete;ChatService& operator=(const ChatService&) = delete;
private:// 注册消息以及对应的Handler回调操作ChatService();// 存储消息id和其对应的业务处理方法unordered_map<int,MsgHandler> m_msgHandlerMap;
};#endif // CHATSERVICE_H
  • chatservice.cpp
#include "chatservice.hpp"
#include "public.hpp"
#include <muduo/base/Logging.h>
using namespace muduo;// 获取单例对象的接口函数 线程安全的单例对象
ChatService* ChatService::getInstance() {static ChatService service;return &service;
}// 注册消息以及对应的Handler回调操作
ChatService::ChatService() {m_msgHandlerMap.insert({LOGIN_MSG,std::bind(&ChatService::login, this, _1, _2, _3)});  m_msgHandlerMap.insert({REG_MSG,std::bind(&ChatService::reg, this, _1, _2, _3)});  
}// 处理登录业务
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!!!";
}// 获取消息对应的处理器
MsgHandler ChatService::getHandler(int msgid) {// 记录错误日志,msgid没有对应的事件处理回调auto it = m_msgHandlerMap.find(msgid);if(it == m_msgHandlerMap.end()) {// 返回一个默认的处理器,空操作return [=](const TcpConnectionPtr &conn, json &js, Timestamp) {LOG_ERROR << "msgid:" << msgid << " can not find handler!";};}else {return m_msgHandlerMap[msgid];}
}

三、src/server目录下的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;
}

四、thirdparty目录下是json.hpp

下载:GitHub - nlohmann/json: JSON for Modern C++

在single_include/nlohmann里头有一个json.hpp,把它放到我们的项目中就可以了

五、CMake构建项目

(1)在src/server目录中的CMakeLists.txt

# 定义了一个SRC_LIST变量 包含了该目录下所有的源文件
aux_source_directory(. SRC_LIST)
# 指定生成可执行文件
add_executable(ChatServer ${SRC_LIST})
# 指定可执行文件链接时需要依赖的库文件
target_link_libraries(ChatServer muduo_net muduo_base pthread)

(2)在src目录下的CMakeLists.txt

add_subdirectory(server)

(3)与include和src,以及thirdparty同级目录的CMakeLists.txt

cmake_minimum_required(VERSION 3.28.0)
project(chat)# 配置编译选项
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -g)# 配置可执行文件生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)# 配置头文件搜索路径
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PROJECT_SOURCE_DIR}/include/server)
include_directories(${PROJECT_SOURCE_DIR}/thirdparty)# 加载子目录
add_subdirectory(src)

在此目录下打开终端,执行命令:

cmake -B build
cmake --build build

接着执行

./bin/ChatServer

打开另一个终端,执行

telnet 127.0.0.1 6000
输入以下内容进行测试:
{"msgid":1}
{"msgid":2}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/449860.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kubeadm安装K8S_v1.28.x容器使用docker

一&#xff0e;环境部署 1.1 基础环境配置&#xff08;只有1台服务器&#xff0c;作为masrer&#xff0c;也作为node使用&#xff09; [rootecs-cf5e ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootecs-cf5e ~]# uname -a Linux ecs-cf5e 3.10.0-11…

算法设计与分析实验:动态规划与回溯

目录​​​​​​​ 一、编辑距离 1.1 具体思路 1.2 思路展示 1.3 代码实现 1.4 复杂度分析 1.5 运行结果 二、买卖股票的最佳时机 2.1 具体思路 2.2 思路展示 2.3 代码实现 2.4 复杂度分析 2.5 运行结果 三、单词拆分 3.1 具体思路 3.2 思路展示 3.3 代码实现…

Leetcode206:反转链表

一、题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表 示例&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]输入&#xff1a;head [] 输出&#xff1…

Linux mount

挂载移动硬盘 1、通过 命令 fdisk -l 查看移动硬盘 2、创建 挂载点及文件 mkdir zen 3、mount -t ntfs /dev/sdb1 zen 报错&#xff1a;mount: unknown filesystem type ‘ntfs’ 需要安装ntfs-3g 如下才用编译安装方法&#xff1a; wget https://tuxera.com/opensource/ntf…

【Spark实践6】特征转换FeatureTransformers实践Scala版--补充算子

本节介绍了用于处理特征的算法&#xff0c;大致可以分为以下几组&#xff1a; 提取&#xff08;Extraction&#xff09;&#xff1a;从“原始”数据中提取特征。转换&#xff08;Transformation&#xff09;&#xff1a;缩放、转换或修改特征。选择&#xff08;Selection&…

2024年第三届能源与环境工程国际会议(CFEEE 2024) | Ei&Scopus双检索

会议简介 Brief Introduction 2024年第三届能源与环境工程国际会议(CFEEE 2024) 会议时间&#xff1a;2024年12月12日-14日 召开地点&#xff1a;澳大利亚凯恩斯 大会官网&#xff1a;CFEEE 2024-2024 International Conference on Frontiers of Energy and Environment Engine…

【乳腺肿瘤诊断分类及预测】基于Elman神经网络

课题名称&#xff1a;基于Elman神经网络的乳腺肿瘤诊断分类及预测 版本日期&#xff1a;2023-05-15 运行方式: 直接运行Elman0501.m 文件即可 代码获取方式&#xff1a;私信博主或QQ&#xff1a;491052175 模型描述&#xff1a; 威斯康辛大学医学院经过多年的收集和整理&a…

20240202在WIN10下使用whisper.cpp

20240202在WIN10下使用whisper.cpp 2024/2/2 14:15 【结论&#xff1a;在Windows10下&#xff0c;确认large模式识别7分钟中文视频&#xff0c;需要83.7284 seconds&#xff0c;需要大概1.5分钟&#xff01;效率太差&#xff01;】 83.7284/4200.1993533333333333333333333333…

云计算基础(云计算概述)

目录 一、云计算概述 1.1 云计算的概念 1.1.1 云计算解决的问题 1.1.2 云计算的概念 1.1.3 云计算的组成 1.2 云计算主要特征 1.2.1 按需自助服务 1.2.2 泛在接入 1.2.3 资源池化 1.2.4 快速伸缩性 1.2.5 服务可度量 1.3 云计算服务模式 1.3.1 软件即服务(Softwar…

海外IP代理:解锁网络边界的实战利器

文章目录 引言&#xff1a;正文&#xff1a;一、Roxlabs全球IP代理服务概览特点&#xff1a;覆盖范围&#xff1a;住宅IP真实性&#xff1a;性价比&#xff1a;在网络数据采集中的重要性&#xff1a; 二、实战应用案例一&#xff1a;跨境电商竞品分析步骤介绍&#xff1a;代码示…

蓝桥杯备战——12.PCF8591芯片的使用

目录 1.芯片简介2.读写时序3.控制字4.代码封装库5.原理图分析6.使用示例 1.芯片简介 截取自NXP的PCF8591芯片数据手册&#xff0c;我把重点关注部分划出来了&#xff0c;请务必自行阅读一遍数据手册&#xff01; 2.读写时序 ①器件地址&#xff1a; Bit0决定是读还是写操作&…

mcu短时间内发生多次中断,如何解决中断丢失问题?

问题 嵌入式开发中&#xff0c;如果中断A的处理函数执行时间长&#xff0c;某段时间内&#xff0c;快速来了2个中断A(例如&#xff1a;外部管脚输入信号变化)&#xff0c;则会导致第2个中断丢失。 我有几个疑问&#xff1a; 1.目前市面上的芯片&#xff0c;是否支持缓存中断标志…