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 time
    auto 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}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

5分钟快速掌握 XML (Extensible Markup Language)

背景 在Java开发的过程中&#xff0c;我们经常需要和配置文件打交道&#xff0c;其中接触最多的就是XML。从最初学习 JavaWeb 时在 Tomcat 中配置servlet&#xff0c;到后来接触Spring框架并在XML中编写各种配置&#xff0c;XML一直是不可或缺的一部分。然而&#xff0c;XML的…

在Vue中如何构建复杂表单?

概述 很有可能&#xff0c;在我们的软件工程旅程中&#xff0c;我们至少要构建一次复杂的表单。本文将介绍如何创建一个复杂的表单&#xff0c;该表单可以使用一些Vue特性(如v-for和v-model)逐步增强。它还提供了一些基本的Vue核心功能的复习&#xff0c;这些功能将在您日常使…

MySQL中去除重复(十一)

MySQL中去除重复(十一) 一、相同的行 我们要去除相同行要使用DISTINCT关键字 SELECT DISTINCT 列名 FROM 表名; distinct 是针对查询的结果集合进行去重而不是针对某一行或者某一列。 二、查询中的行选择 用 WHERE 子句限制从查询返回的行。一个 WHERE 子句包含一个 必须满…

【Matplotlib】figure方法之图形的保存

&#x1f388;个人主页&#xff1a;甜美的江 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;matplotlib &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

在flutter中集成Excel导入和导出

flutter中集成Excel导入和导出功能 1、需要的依赖 在pubspec.yaml #excel导出syncfusion_flutter_xlsio: ^24.1.45open_file: ^3.0.1#导入excelflutter_excel: ^1.0.1#选择文件的依赖file_picker: ^6.1.1&#xff08;1&#xff09;依赖说明 在测试时&#xff0c;我们在使用导…

Faster-Whisper 实时识别电脑语音转文本

Faster-Whisper 实时识别电脑语音转文本 前言项目搭建环境安装Faster-Whisper下载模型编写测试代码运行测试代码实时转写脚本实时转写WebSocket服务器模式 参考 前言 以前做的智能对话软件接的Baidu API&#xff0c;想换成本地的&#xff0c;就搭一套Faster-Whisper吧。 下面是…

25考研|660/880/1000/1800全年带刷计划

作为一个参加过两次研究生考试的老学姐&#xff0c;我觉得考研数学的难度完全取决于你自己 我自己就是一个很好的例子 21年数学题目是公认的简单&#xff0c;那一年考130的很多&#xff0c;但是我那一年只考了87分。但是22年又都说是有史以来最难的一年&#xff0c;和20年的难度…

centos 7 部署若依前后端分离项目

目录 一、新建数据库 二、修改需求配置 1.修改数据库连接 2.修改Redis连接信息 3.文件路径 4.日志存储路径调整 三、编译后端项目 四、编译前端项目 1.上传项目 2.安装依赖 3.构建生产环境 五、项目部署 1.创建目录 2.后端文件上传 3. 前端文件上传 六、服务启…

Linux信号详解~

目录 前言 一、初识信号 二、信号的概念 三、信号的发送与捕捉 3.1 信号的发送 3.1.1 kill 命令 3.1.2 kill 函数 3.1.3 raise函数 3.1.4 abort函数 3.2 信号的捕捉 3.2.1 signal函数 3.2.2 sigaction函数 3.2.3 图示 四、信号的产生 4.1 硬件异常产生信号 4.2 …

C++输出地址

下面是一段输出地址的程序。 #include <bits/stdc.h> using namespace std;int main() {int s;cout << &s;//原地址return 0; }假如有一个人&#xff08;的朋友&#xff09;后来了&#xff0c;他也想住进的房间&#xff0c;我们可以这样&#xff1a; #includ…

OfficeWeb365 Readfile 任意文件读取漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

windows下安装go

下载golang Go 官网下载地址&#xff1a; https://golang.org/dl/ Go 官方镜像站&#xff08;推荐&#xff09;&#xff1a; https://golang.google.cn/dl/ 选择安装包 验证有没有安装成功 查看 go 环境 说明 &#xff1a; Go1.11 版本之后无需手动配置环境变量&#xff0c…

Apache POl Excel

目录 介绍 Apache POl的应用场景&#xff1a; 入门使用 通过POI创建Excel文件并且写入文件内容 通过POI读取Excel文件中的内容 介绍 Apache POl是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是&#xff0c;我们可以使用POI在Java程序中对Miscrosoft O…

VBA技术资料MF114:批量给Word文档添加页眉

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

Acwing 141 周赛 解题报告 | 珂学家 | 逆序数+奇偶性分析

前言 整体评价 很普通的一场比赛&#xff0c;t2思维题&#xff0c;初做时愣了下&#xff0c;幸好反应过来了。t3猜猜乐&#xff0c;感觉和逆序数有关&#xff0c;和奇偶性有关。不过要注意int溢出。 欢迎关注: 珂朵莉的天空之城 A. 客人数量 题型: 签到 累加和即可 import…

简单说说mysql的日志

今天我们通过mysql日志了解mysqld的错误日志、慢查询日志、二进制日志&#xff0c;redolog, undolog等。揭示它们的作用和用途&#xff0c;让我们工作中更能驾驭mysql。 redo 日志 如果mysql事务提交后发生了宕机现象&#xff0c;那怎么保证数据的持久性与完整性&#xff1f;…

JAVA基础 队列

排队取奶茶 时间限制&#xff1a;1.000S 空间限制&#xff1a;128MB 题目描述 假设有一家奶茶店&#xff0c;现在有一些人在排队等待取奶茶&#xff0c;同时也有人在取奶茶。 请你设计一个程序模拟这种情况下的奶茶队列管理。 假设每个人取奶茶的时间非常短&#xff0c;可…

MySQL亿级数据的查询优化-历史表该如何建

前端时间在知乎上看到一个问题&#xff0c;今天有空整理并测试了一下&#xff1a; 这个问题很具体&#xff0c;所以还是可以去尝试优化一下&#xff0c;我们基于InnoDB并使用自增主键来讲。 比较简单的做法是将历史数据存放到另一个表中&#xff0c;与最近的数据分开。那是不是…

DockerCompose+SpringBoot+Nginx+Mysql实践

DockerComposeSpringBootNginxMysql实践 1、Spring Boot案例 首先我们先准备一个 Spring Boot 使用 Mysql 的小场景&#xff0c;我们做这样一个示例&#xff0c;使用 Spring Boot 做一个 Web 应 用&#xff0c;提供一个按照 IP 地址统计访问次数的方法&#xff0c;每次请求时…

【C语言/基础梳理/期末复习】动态内存管理(附思维导图)

目录 一、为什么要有动态内存分配 &#xff08;1&#xff09;我们已经掌握的内存方式的特点 &#xff08;2&#xff09;需求 二、malloc和free 2.1.malloc 2.1.1函数原型 2.1.2函数使用 2.1.3应用示例​编辑 2.2free 2.2.1函数原型 2.2.2函数使用 三、calloc和reallo…