集群聊天服务器项目(一)——模块分层设计

news/2025/2/13 21:22:25/

本项目对程序不同功能进行分层设计,分为网络层、业务层、和数据层。

C++面向接口编程也就是面向抽象类,网络模块和业务模块尽量解耦。

网络层

网络层主要封装的是网络连接方面的一些功能,即socket相关操作,这里该项目采用的是muduo网络库作为网络层的底层支撑,主要是设置连接到来消息到来的回调设置以及服务器基本设置(如子Loop数、启动服务)。

本项目消息使用json格式,通过解析消息格式,来确定调用业务层的某一具体功能:

void ChatServer::onMessage(const TcpConnectionPtr &conn,Buffer *buffer,Timestamp time)
{string buf = buffer->retrieveAllAsString();// 数据的反序列化json js = json::parse(buf);auto msgHandler = ChatService::instance()->getHandler(js["msgid"].get<int>());// 回调指定的绑定好的事件处理器,来执行相应的业务处理msgHandler(conn, js, time);
}

业务层

业务层主要处理具体的业务,如登录业务、注册业务、一对一聊天业务、群聊业务等,其中业务类ChatService是一个单例模式,其中使用unordered_map存储函数表,通过消息id来映射具体的业务处理函数。还要用一个unordered_map来存储用户id与其对应的TcpConnectionPtr,使其能够对某一客户端连接进行IO操作。

对于某一具体业务,以登录业务举例,其流程如下

在这里插入图片描述

// 处理登录业务
// 检测id 和 pwd是否在user表中存在,并将状态修改
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
{int id = js["id"].get<int>();string pwd = js["password"];User user = _userModel.query(id);if (user.getId() == id && user.getPwd() == pwd){if (user.getState() == "online"){// 该用户已登录,不允许重复登录json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 2;response["errmsg"] = "The account has already been logged in. Please enter again.";conn->send(response.dump());}else{// 登录成功,记录用户连接信息{// 锁粒度不要太大,过大就丧失并发性lock_guard<mutex> lock(_connMutex);_userConnMap.insert({id, conn});}// 用户登录成功后向redis订阅channel (用户id)_redis.subscribe(id);// 登录成功,更新用户状态信息json response;user.setState("online");_userModel.updateState(user);response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 0;response["id"] = user.getId();response["name"] = user.getName();// 查询该用户是否有离线消息,若有则读取vector<string> vec = _offlineMsgModel.query(id);if (!vec.empty()){response["offlinemsg"] = vec;// 读取该用户离线消息后,把该用户所有离线消息从数据库中删除_offlineMsgModel.remove(id);}// 查询该用户好友信息并返回vector<User> userVec = _friendModel.query(id);if (!userVec.empty()){vector<string> vec2;for (User &user : userVec){json js;js["id"] = user.getId();js["name"] = user.getName();js["state"] = user.getState();vec2.push_back(js.dump());}response["friends"] = vec2; // 嵌套使用}// 查询用户群组信息,一个用户有多个群组,一个群组有多个组员vector<Group> groupuserVec = _groupModel.queryGroups(id);if (!groupuserVec.empty()){// "group":[{groupid:[xxx, xxx, xxx, xxx]}]vector<string> groupV; // 存储一个用户的所有组的信息for (Group &group : groupuserVec){json grpjson;grpjson["id"] = group.getId();grpjson["groupname"] = group.getName();grpjson["groupdesc"] = group.getDesc();vector<string> userV; // 存储一个组内所有组员信息// 遍历每个组内的所有组员信息for (GroupUser &user : group.getUsers()){json js;js["id"] = user.getId();js["name"] = user.getName();js["state"] = user.getState();js["role"] = user.getRole();userV.push_back(js.dump());}grpjson["users"] = userV;groupV.push_back(grpjson.dump());}response["groups"] = groupV;}conn->send(response.dump());}}else{// 登录失败,密码错误json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 1;response["errmsg"] = "id or password is invalid!";conn->send(response.dump());}
}

其他业务处理函数都类似。

数据层

为了使数据模块业务模块分离

加入 ORM(object Relation Model)类也就是将表的字段封装为一个类并提供对应的 getset 方法,业务层操作的都是对象,DAO层(数据访问层)即xxxmodel类才访问数据。

例如,userModel层提供的方法接受的数据都是User对象,而不是直接裸数据传递.

model类是db类和ORM类的桥梁,model类使用db类提供的方法,使用ORM类对象成员进行SQL的CRUD操作。

数据库的表设计

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

一般来讲,本项目在表数据量为5w以内都能比较高效的进行表查询操作而不需要修改表结构或者是分库分表操作。


http://www.ppmy.cn/news/49454.html

相关文章

【C++】海量数据面试题

海量数据面试题 文章目录 海量数据面试题一、哈希切割二、位图应用1.给定100亿个整数&#xff0c;设计算法找到只出现一次的整数2.求两个文件交集3.在100亿个整数中找到出现次数不超过2次的所有整数 三、布隆过滤器1.求两文件交集&#xff08;近似算法&#xff09;2.求两文件交…

vue2项目PC端如何适配不同分辨率屏幕

项目构建&#xff1a;基于vue-cli3构建&#xff0c;使用postcss-px2rem px2rem-loader进行rem适配 实现原理&#xff1a;每次打包&#xff0c;webpack通过使用插件postcss-px2rem&#xff0c;帮我们自动将px单位转换成rem单位前方有坑&#xff1a;UI框架部分组件使用JavaScript…

Thinkphp+vued大学生租房管理系统mysql校园房屋租赁网站系统

学生租房管理系统是计算机技术和网络迅速发展的一个大学生租房信息应用解决方案。大学生租房平台将Internet网络技术与现代管理观念相融合&#xff0c;针对信息技术的特点对大学生租房平台进行规划和重构&#xff0c;对大学生租房信息流进行优化及合理配置&#xff0c;生成动态…

云原生(docker+k8s+阿里云)

Gitee-Kubernetes学习 kubectl备忘清单 k8s官方文档-task [云原生-kubectl命令详解] ingress详解 ingress官方文档 云原生-语雀-架构师第一课 如上图&#xff0c;服务器有公网ip和私网ip&#xff0c;公网ip是外部访问服务器用的&#xff0c;重启一次实例就变化了&#xff0c;如…

基于JavaSpringMvc+mybatis实现学生信息管理系统

基于JavaSpringMvcmybatis实现学生信息管理系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目…

Infura的基本用途和具体实例

文章目录 Infura 可以做什么&#xff1f;1. 向以太坊网络发送交易并获取交易的结果2. 获取以太坊地址的余额、交易历史记录等信息3. 通过 Web3.js 等以太坊库与智能合约进行交互3. 使用 Infura 发送以太币4. 其他服务 Infura 是一个由 ConsenSys 开发的以太坊基础设施服务提供商…

反向传播推导+numpy实现

很久没有看深度学习了&#xff0c;忘了好多东西。本来想着推导一下&#xff0c;后来发现自己不会了。 再看看以前写的代码&#xff0c;又避开了最终的东西&#xff0c;于是决定重新推导一下。 数据的说明 首先&#xff0c;我们要做一个回归的任务&#xff0c;我们使用numpy随…

《Java8实战》第12章 新的日期和时间 API

原来的Java的时间类Date、java.util.Calendar类都不太好&#xff0c;以语言无关方式格式化和解析日期或时间的 DateFormat 方法也有线程安全的问题 12.1 LocalDate、LocalTime、LocalDateTime、Instant、Duration 以及 Period 12.1.1 使用 LocalDate 和 LocalTime LocalDate…