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.mzph.cn/news/662451.shtml

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

相关文章

jsp 产品维修管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 产品维修管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.…

如何使用内网穿透工具在公网实现实时监测DashDot服务器仪表盘

文章目录 1. 本地环境检查1.1 安装docker1.2 下载Dashdot镜像 2. 部署DashDot应用3. 本地访问DashDot服务4. 安装cpolar内网穿透5. 固定DashDot公网地址 本篇文章我们将使用Docker在本地部署DashDot服务器仪表盘&#xff0c;并且结合cpolar内网穿透工具可以实现公网实时监测服务…

2024 TikTok Shop本土店入驻流程全解,建议收藏

如果要在2023选出最具潜力的跨境电商平台&#xff0c;TikTok Shop无疑是一个佼佼者。从上线全托管模式初出锋芒&#xff0c;再到遭遇印尼、东南亚政策打击&#xff0c;最后在黑五电商大促中取得辉煌成绩。2024TikTok势必是红海一片&#xff0c;现在上车还来得及&#xff01;下面…

SpringBoot security 安全认证(三)——自定义注解实现接口放行配置

背景&#xff1a;通过Security实现了安全管理&#xff0c;可以配置哪些接口可以无token直接访问。但一个麻烦就是每增加一个匿名访问接口时都要去修改SecurityConfig配置&#xff0c;从程序设计上讲是不太让人接受的。 本节内容&#xff1a;即是解决以上问题&#xff0c;增加一…

Model Checking Guided Testing for Distributed Systems——论文泛读

EuroSys 2023 Paper 论文阅读笔记整理 问题 分布式系统已成为云计算的支柱&#xff0c;不正确的系统设计和实现可能严重影响分布式系统的可靠性。尽管使用形式化规范建模的分布式系统设计可以通过形式化模型检查进行验证&#xff0c;但要弄清其相应的实现是否符合已验证的规范…

【EI会议征稿通知】第三届信号处理与通信安全国际学术会议(ICSPCS 2024)

第三届信号处理与通信安全国际学术会议&#xff08;ICSPCS 2024&#xff09; 2024 3rd International Conference on Signal Processing and Communication Security 信号处理和通信安全是现代信息技术应用的重要领域&#xff0c;近年来这两个领域的研究相互交叉促进&#xf…

【机器学习】贝叶斯垃圾邮件识别

实验三&#xff1a;贝叶斯垃圾邮件识别 本次作业以垃圾邮件分类任务为基础&#xff0c;要求提取文本特征并使用朴素贝叶斯算法进行垃圾邮件识别&#xff08;调用已有工具包或自行实现&#xff09;。 1 任务介绍 ​ 电子邮件是互联网的一项重要服务&#xff0c;在大家的学习、…

【ADI 知识库】X 波段相控阵开发平台 硬件 2

ADAR1000EVAL1Z (Stingray) ADAR1000-EVAL1Z评估板是一款模拟波束成形前端&#xff0c;设计用于测试ADAR1000和ADTR1107的性能。ADAR1000 是一款 8 GHz 至 16 GHz、4 通道、X 波段和 Ku 波段波束成形器 IC。ADTR1107是 6 GHz 至 18 GHz 前端发送/接收模块。 ADAR1000-EVAL1Z板…

网络异常案例五_SYN被丢弃

问题现象 公司同事使用的时候&#xff0c;反馈系统不稳定&#xff0c;访问的时候&#xff0c;有时候会出现白屏&#xff08;连接超时&#xff09;&#xff0c;或者系统页面点击没有响应&#xff0c;过一会之后刷新系统又可以正常展示了。之前未收到过类似反馈&#xff0c;一直…

Axure 动态面板初使用 - 实现简单的Banner图轮播效果

使用工具版本 Axure 9 实现的效果 步骤过程 1、打开Axure工具&#xff0c;从元件库拖个动态面板到空白页&#xff1b; 2、给面板设置一个常用的banner尺寸&#xff0c;举个栗子&#xff1a;343151(移动端我常用的banner尺寸)&#xff0c;顺便给它起个名字&#xff0c;就叫…

QT学习日记 | 信号与槽

目录 前言 一、初始信号与槽 1、信号与槽的本质 2、信号与槽的使用 3、内置信号、内置槽函数与自定义信号、自定义槽函数 &#xff08;1&#xff09;文档查询 &#xff08;2&#xff09;自定义信号与内置槽函数的使用 4、信号与槽函数关联关系 5、带参数的信号与槽函数…

【软件设计师笔记】程序语言设计考点

【考证须知】IT行业高含金量的证书(传送门)&#x1f496; 【软件设计师笔记】计算机系统基础知识考点(传送门)&#x1f496; 【软件设计师笔记】操作系统考点(传送门)&#x1f496; &#x1f413; 编程语言之间的翻译形式 汇编 高级程序不能直接在计算机上执行&#xff0c;…

yolov8训练自己的关键点检测模型

参考&#xff1a; https://blog.csdn.net/weixin_38807927/article/details/135036450 标注数据集 安装labelme pip install labelme -i https://pypi.tuna.tsinghua.edu.cn/simple如果报错 $ labelme 2024-01-31 03:16:20,636 [INFO ] __init__:get_config:67- Loading …

YOLOv5改进系列(29)——添加DilateFormer(MSDA)注意力机制(中科院一区顶刊|即插即用的多尺度全局注意力机制)

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

面试宝典之深谈JVM

面试宝典之深谈JVM 1.为什么需要JVM&#xff0c;不要JVM可以吗&#xff1f; 1.JVM可以帮助我们屏蔽底层的操作系统 一次编译&#xff0c;到处运行 2.JVM可以运行Class文件 2.JDK&#xff0c;JRE以及JVM的关系 3.我们的编译器到底干了什么事&#xff1f; 仅仅是将我们的 .ja…

深入理解 Golang 的 crypto/elliptic:椭圆曲线密码学的实践指南

深入理解 Golang 的 crypto/elliptic&#xff1a;椭圆曲线密码学的实践指南 引言crypto/elliptic 库概览基本使用教程高级应用案例性能与安全考量结论 引言 在当今数字时代&#xff0c;数据安全和加密技术成为了信息技术领域的重中之重。特别是在网络通信和数据存储领域&#…

如何系统的自学Python?通义千问、讯飞星火、文心一言及ChatGPT的回答

如何系统的自学Python&#xff1f;来看看通义千问、讯飞星火、文心一言及ChatGPT的回答. 第一个是马老师的通义千问 系统地自学Python是一个循序渐进的过程&#xff0c;从基础语法到实践项目&#xff0c;再到专业领域的深入学习。下面是一个详细的步骤指南&#xff1a; 了解Py…

控制台npm start终止不了?

控制台npm start终止不了&#xff1f; 在开发的过程中我遇到了这样的问题&#xff0c;想结束控制台3002端口运行&#xff0c;但是ControlC不起作用&#xff0c;不管我敲多少遍&#xff0c;依旧没有任何动静&#xff1a; 再次启动的时候它又会自动启动3003端口&#xff0c;300…

Kotlin 协程:深入理解 ‘async { }‘

Kotlin 协程&#xff1a;深入理解 ‘async { }’ Kotlin 协程是一种强大的异步编程工具&#xff0c;它提供了一种简洁、易读的方式来处理并发和异步操作。在 Kotlin 协程库中&#xff0c;async {} 是一个关键的函数&#xff0c;它允许我们启动一个新的协程&#xff0c;并返回一…

【大厂AI课学习笔记】1.4 算法的进步(2)

关于感知器的兴衰。 MORE&#xff1a; 感知器的兴衰 一、感知器的发明与初期振动 在人工智能的历史长河中&#xff0c;感知器&#xff08;Perceptron&#xff09;无疑是一个里程碑式的存在。它最初由心理学家Frank Rosenblatt在1950年代提出&#xff0c;并在随后的几年中得到…