当前位置:   article > 正文

C++项目——集群聊天服务器项目(五)网络模块与业务模块

C++项目——集群聊天服务器项目(五)网络模块与业务模块

今天来正式书写集群聊天服务器网络模块与部分业务模块的代码

环境搭建C++项目——集群聊天服务器项目(一)项目介绍、环境搭建、Boost库安装、Muduo库安装、Linux与vscode配置-CSDN博客

Json第三方库C++项目——集群聊天服务器项目(二)Json第三方库-CSDN博客

muduo网络库C++项目——集群聊天服务器项目(三)muduo网络库-CSDN博客

MySQL数据库C++项目——集群聊天服务器项目(四)MySQL数据库-CSDN博客

一、工程目录创建

项目通过CMake编译,书写CMakeLists.txt文件,分别书写三级,从项目目录中一级一级往下找

(1)项目根目录下CHAT

  1. cmake_minimum_required(VERSION 3.0)
  2. project(chat)
  3. # 配置编译选项
  4. set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -g) # 可调试可执行文件
  5. # 设置可执行文件存储的路径
  6. set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
  7. #配置头文件搜索路径
  8. include_directories(${PROJECT_SOURCE_DIR}/include)
  9. include_directories(${PROJECT_SOURCE_DIR}/include/server)
  10. include_directories(${PROJECT_SOURCE_DIR}/thirdparty)
  11. # 加载子目录
  12. add_subdirectory(src)

(2)子目录src中

add_subdirectory(server)

(3)src目录中server目录

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

二、网络模块代码编写

同前述集群聊天服务器项目(三)中服务器代码步骤一致,即

1.组合TcpServer对象

2.创建EventLoop事件循环对象的指针

3.明确TcpServer构造函数需要什么参数,输出ChatServer的构造函数

4.在当前服务器类的构造函数中,注册处理连接和读写事件的回调函数

5.设置合适的服务端线程数量,muduo会自动划分I/O线程和worker线程

由于是项目编写,因此将头文件与源文件区分开

在项目根目录/include/server下编写chatserver.hpp头文件

  1. #ifndef CHATSERVER_H
  2. #define CHATSERBER_H
  3. #include <muduo/net/EventLoop.h>
  4. #include <muduo/net/TcpServer.h>
  5. using namespace muduo;
  6. using namespace muduo::net;
  7. //聊天服务器的主类
  8. class ChatServer
  9. {
  10. public:
  11. // 初始化聊天服务器对象
  12. ChatServer(EventLoop *loop,
  13. const InetAddress &listenAddr,
  14. const string &nameArg);
  15. // 启动服务
  16. void start();
  17. private:
  18. //上报链接相关信息的回调函数
  19. void onConnection(const TcpConnectionPtr &);
  20. //上报读写相关信息的回调函数
  21. void onMessage(const TcpConnectionPtr &,
  22. Buffer *,
  23. Timestamp);
  24. TcpServer _server; //组合muduo库,实现服务器功能的类对象
  25. EventLoop *_loop; //指向事件循环单元的指针
  26. };
  27. #endif

相应的,在项目根目录/src/server下编写chatserver.cpp源文件

  1. #include "chatserver.hpp"
  2. #include "json.hpp"
  3. #include "chatservice.hpp"
  4. #include <iostream>
  5. #include <functional>
  6. #include <string>
  7. using namespace std;
  8. using namespace placeholders;//占位符
  9. using json = nlohmann::json;
  10. //初始化聊天服务器对象
  11. ChatServer::ChatServer(EventLoop *loop,
  12. const InetAddress &listenAddr,
  13. const string &nameArg)
  14. : _server(loop, listenAddr, nameArg), _loop(loop)
  15. {
  16. //注册链接回调
  17. _server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));//绑定器bind
  18. //注册消息回调
  19. _server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));//绑定器bind
  20. //设置线程数量4
  21. _server.setThreadNum(4);
  22. }
  23. //启动服务
  24. void ChatServer::start()
  25. {
  26. _server.start();
  27. }
  28. //上报链接相关信息的回调函数
  29. void ChatServer::onConnection(const TcpConnectionPtr &conn)
  30. {
  31. //客户端断开链接
  32. if (!conn->connected())
  33. {
  34. conn->shutdown();//关闭文件描述符
  35. }
  36. }
  37. //上报读写事件相关信息的回调函数,收到消息了,
  38. void ChatServer::onMessage(const TcpConnectionPtr &conn,
  39. Buffer *buffer,
  40. Timestamp time)
  41. {
  42. string buf = buffer->retrieveAllAsString();//转成字符串接收
  43. //测试,添加json打印代码
  44. cout << buf << endl;
  45. //数据的反序列化
  46. json js = json::parse(buf);
  47. //达到的目的:完全解耦网络模块的代码和业务模块的代码
  48. //通过js["msgid"] 获取=》业务handler处理器(在业务模块事先绑定好的)=》conn js time传给你
  49. auto msgHandler = ChatService::instance()->getHandler(js["msgid"].get<int>());//转成整型
  50. //回调消息绑定好的事件处理器,来执行相应的业务处理,一个ID一个操作
  51. msgHandler(conn, js, time);
  52. }

在这里,网络模块与业务模块彻底解耦

msgHandler是事件处理器,定义在业务模块,通过js["msgid"] 获取=》业务handler处理器(在业务模块事先绑定好的)=》conn  js  time

三、业务模块代码编写

目前先暂且编写登录和注册回调函数测试程序是否正常运转,后续进行完善

在项目根目录/include/server下编写chatservice.hpp头文件

定义聊天服务器业务类对象,绑定相应的事件回调函数,由于只需要一个构造函数,进行单例化操作

chatservice.hpp

  1. #ifndef CHATSERVICE_H
  2. #define CHATSERVICE_H
  3. #include <muduo/net/TcpConnection.h>
  4. #include <unordered_map>//一个消息ID映射一个事件处理
  5. #include <functional>
  6. using namespace std;
  7. using namespace muduo;
  8. using namespace muduo::net;
  9. #include "json.hpp"
  10. using json = nlohmann::json;
  11. //表示处理消息的事件回调方法类型,事件处理器,派发3个东西
  12. using MsgHandler = std::function<void(const TcpConnectionPtr &conn, json &js, Timestamp)>;
  13. //聊天服务器业务类
  14. class ChatService
  15. {
  16. public:
  17. //获取单例对象的接口函数
  18. static ChatService *instance();
  19. //处理登录业务
  20. void login(const TcpConnectionPtr &conn, json &js, Timestamp time);
  21. //处理注册业务
  22. void reg(const TcpConnectionPtr &conn, json &js, Timestamp time);
  23. //获取消息对应的处理器
  24. MsgHandler getHandler(int msgid);
  25. private:
  26. ChatService();//单例
  27. //存储消息id和其对应的业务处理方法,消息处理器的一个表,写消息id对应的处理操作
  28. unordered_map<int, MsgHandler> _msgHandlerMap;
  29. };
  30. #endif

定义枚举量进行哈希表索引,通过不同的枚举量调用响应的业务处理方法

在项目根目录/include/server下编写public.hpp头文件

public.hpp

  1. #ifndef PUBLIC_H
  2. #define PUBLIC_H
  3. /*
  4. Server 和 Client 的 公共文件
  5. */
  6. enum enMsgType{
  7. LOGIN_MSG = 1,//登录消息
  8. REG_MSG, //注册消息
  9. };
  10. #endif

相应的,在项目根目录/src/server下编写chatservice.cpp源文件

在服务器业务类将事件处理函数与枚举量插入定义的容器中,以便后续通过msgid进行索引

  1. #include "chatservice.hpp"
  2. #include "public.hpp"
  3. #include <muduo/base/Logging.h>//muduo的日志
  4. using namespace std;
  5. using namespace muduo;
  6. //获取单例对象的接口函数
  7. ChatService *ChatService::instance()
  8. {
  9. static ChatService service;
  10. return &service;
  11. }
  12. //构造方法,注册消息以及对应的Handler回调操作
  13. ChatService::ChatService()
  14. {
  15. //用户基本业务管理相关事件处理回调注册
  16. _msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});
  17. _msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});
  18. }
  19. //获取消息对应的处理器
  20. MsgHandler ChatService::getHandler(int msgid)
  21. {
  22. //记录错误日志,msgid没有对应的事件处理回调
  23. auto it = _msgHandlerMap.find(msgid);
  24. if (it == _msgHandlerMap.end())//找不到
  25. {
  26. //返回一个默认的处理器,空操作,=按值获取
  27. return [=](const TcpConnectionPtr &conn, json &js, Timestamp) {
  28. LOG_ERROR << "msgid:" << msgid << " can not find handler!";//muduo日志会自动输出endl
  29. };
  30. }
  31. else//成功的话
  32. {
  33. return _msgHandlerMap[msgid];//返回这个处理器
  34. }
  35. }
  36. //处理登录业务 id pwd pwd
  37. void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
  38. {
  39. LOG_INFO<<"do login service!!!";
  40. }
  41. //处理注册业务 name password
  42. void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp time)
  43. {
  44. LOG_INFO<<"do reg service!!!";
  45. }

登录和注册业务仅打印日志信息

四、main函数

  1. #include "chatserver.hpp"
  2. int main(){
  3. EventLoop loop;
  4. InetAddress addr("127.0.0.1",6000);
  5. ChatServer server(&loop,addr,"ChatServer");
  6. server.start();
  7. loop.loop();
  8. return 0;
  9. }

五、功能测试

监听127.0.0.1 6000端口

可以看到msgid为1时,触发登录回调函数、msgid为2时,触发注册回调函数,由于msgid为3时没有定义,在哈希表中查不到相应的值,打印:找不到相应的处理器,返回空处理对象

功能验证成功!

如果有错误还请联系我,请期待后续的项目更新吧~感谢~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/338377
推荐阅读
相关标签
  

闽ICP备14008679号