C++项目——集群聊天服务器项目(十一)服务器异常退出与添加好友业务

本节来实现C++集群聊天服务器项目中的服务器异常退出与添加好友业务,一起来试试吧

一、服务器异常退出

在Linux环境下,我们在服务器端使用CTRL+C结束程序执行,即使用CTRL+C让服务器异常退出,这样的后果是本应登录服务器的用户在数据库中在线状态未及时修改为离线,在下次重新登录时会显示用户已登录。

为解决上述问题,编写服务器异常退出函数,只要调用用户操作对象_userModel将所有在线用户状态修改为离线即可

1.1 服务器异常退出代码

代码实现比较简单,在chatservice.hpp中声明函数,在chatservice.cpp中定义

    // 服务端异常,业务重置方法void reset();
// 服务端异常,业务重置方法
void ChatService::reset()
{// 把所有online用户的状态设置为offline_userModel.resetState();
}

在main函数,调用业务模块处理服务器异常退出的函数,signal信号捕获突发事件

#include "chatserver.hpp"
#include "chatservice.hpp"
#include <signal.h>//处理服务器ctrl+c结束程序后,重置用户user的状态信息
void resetHandler(int){ChatService::instance()->reset();exit(0);
}int main()
{signal(SIGINT,resetHandler);EventLoop loop;InetAddress addr("127.0.0.1", 6000);ChatServer server(&loop, addr, "ChatServer");server.start();loop.loop();return 0;
}

1.2 功能实现

使用张三的账号登录服务器

张三已经在线

服务器ctrl+c异常退出

数据库张三状态已修改为离线。

服务器异常退出业务验证成功!

二、添加好友

服务器要实现好友聊天,那么就需要添加好友,才可依据好友id号与您的好友进行通信,本节来实现添加好友操作

2.1 添加好友步骤

(1)从json对象中获取用户userid和欲添加的好友friendid

(2)定义FriendModel类对象操作好友表,编写添加好友与查询好友列表两个函数

// 添加好友关系bool insert(int userid, int friendid);// 返回用户好友列表vector<User> query(int userid);

使用FriendModel类对象向数据库好友表中插入好友关系

(3)添加好友成功,返回响应信息、userid和friendid;添加失败,传回响应信息即可

// 添加好友业务             {"msgid":6,"id":22,"name":"Jiao","friendid":13}
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{int userid = js["id"].get<int>();               //获取用户idint friendid = js["friendid"].get<int>();       //获取好友id// 存储消息bool state = _friendModel.insert(userid, friendid);if(state){//添加好友成功!json response;response["msgid"] = ADD_FRIENG_MSG_ACK;response["error"] = 0;response["errmsg"]="成功添加好友!";response["id"] = userid;response["friendid"] = friendid;conn->send(response.dump());}else{//添加好友失败json response;response["msgid"] = ADD_FRIENG_MSG_ACK;response["error"] = 1;response["errmsg"]="添加好友失败!";conn->send(response.dump());}
}

(4)在登录业务中,返回用户离线消息后,也返回用户的好友表,用户可以选择与指定好友进行聊天啦

 // 查询该用户的好友信息并返回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;}

2.2 添加好友代码

在public.hpp,msgid=6表示添加好友

#ifndef PUBLIC_H
#define PUBLIC_H/*
server和client的公共文件    MSGID值
*/
enum EnMsgType
{LOGIN_MSG = 1, // 1:登录消息LOGIN_MSG_ACK, // 2:登录响应消息REG_MSG,     // 3:注册消息REG_MSG_ACK, // 4:注册响应消息ONE_CHAT_MSG,   // 5:聊天消息ADD_FRIENG_MSG, // 6:添加好友消息ADD_FRIENG_MSG_ACK, // 7:添加好友响应消息
};#endif

在include/server/model中创建friendmodel.hpp,定义操作好友表的类FriendModel

#ifndef FRIENDMODEL_H
#define FRIENDMODEL_H#include "user.hpp"
#include <vector>
using namespace std;// 维护好友信息的操作接口方法
class FriendModel
{
public:// 添加好友关系bool insert(int userid, int friendid);// 返回用户好友列表vector<User> query(int userid);
};#endif

在src/server/friendmodel.cpp中进行实现

#include "friendmodel.hpp"
#include "db.hpp"// 添加好友关系
bool FriendModel::insert(int userid, int friendid)
{char sql[1024] = {0};sprintf(sql, "insert into friend values(%d, %d)", userid, friendid);    //向friend表插入好友关系MySQL mysql;if (mysql.connect()){mysql.update(sql);return true;}return false;
}// 返回用户好友列表
vector<User> FriendModel::query(int userid)
{char sql[1024] = {0};sprintf(sql, "select a.id,a.name,a.state from user a \inner join friend b on b.friendid = a.id where b.userid=%d", userid);   //多表联合查询,返回userid的好友列表信息vector<User> Friendvec;             //定义vector数组,存放userid的好友对象MySQL mysql;if (mysql.connect()){MYSQL_RES *res = mysql.query(sql);if (res != nullptr){MYSQL_ROW row;while((row = mysql_fetch_row(res)) != nullptr){User user;user.setId(atoi(row[0]));user.setName(row[1]);user.setState(row[2]);Friendvec.push_back(user);}mysql_free_result(res);return Friendvec;}}return Friendvec;
}

业务模块chatservice.hpp创建添加好友业务函数

#ifndef CHATSERVICE_H
#define CHATSERVICE_H#include <muduo/net/TcpConnection.h>
#include <unordered_map>            //一个消息ID映射一个事件处理
#include <functional>
#include <mutex>
using namespace std;
using namespace muduo;
using namespace muduo::net;#include "usermodel.hpp"
#include "offlinemessagemodel.hpp"
#include "friendmodel.hpp"
#include "json.hpp"
using json = nlohmann::json;// 表示处理消息的事件回调方法类型,事件处理器,派发3个东西
using MsgHandler = std::function<void(const TcpConnectionPtr &conn, json &js, Timestamp)>;// 聊天服务器业务类
class ChatService
{
public:// 获取单例对象的接口函数static ChatService *instance();// 处理登录业务void login(const TcpConnectionPtr &conn, json &js, Timestamp time);// 处理注册业务void reg(const TcpConnectionPtr &conn, json &js, Timestamp time);// 处理点对点聊天消息void oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time);// 添加好友业务void addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time);// 处理客户端异常退出void clientCloseException(const TcpConnectionPtr &conn);// 服务端异常,业务重置方法void reset();// 获取消息对应的处理器MsgHandler getHandler(int msgid);private:ChatService(); // 单例// 存储消息id和其对应的业务处理方法,消息处理器的一个表,写消息id对应的处理操作unordered_map<int, MsgHandler> _msgHandlerMap;// 存储用户的通信连接unordered_map<int, TcpConnectionPtr> _userConnMap;// 定义互斥锁,保证_userConnMap的线程安全mutex _connMutex;// 数据操作类对象UserModel _userModel;                   //用户操作对象offlineMsgModel _offlineMsgModel;       //离线消息操作对象FriendModel _friendModel;               //好友操作对象
};#endif

chatservice.cpp中实现,在构造函数中插入消息处理器map表,绑定相应的事件回调

#include "chatservice.hpp"
#include "public.hpp"
#include <muduo/base/Logging.h> //muduo的日志
using namespace std;
using namespace muduo;// 获取单例对象的接口函数
ChatService *ChatService::instance()
{static ChatService service;return &service;
}// 构造方法,注册消息以及对应的Handler回调操作
ChatService::ChatService()
{// 用户基本业务管理相关事件处理回调注册_msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});_msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});_msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&ChatService::oneChat, this, _1, _2, _3)});_msgHandlerMap.insert({ADD_FRIENG_MSG, std::bind(&ChatService::addFriend, this, _1, _2, _3)});
}// 获取消息对应的处理器
MsgHandler ChatService::getHandler(int msgid)
{// 记录错误日志,msgid没有对应的事件处理回调auto it = _msgHandlerMap.find(msgid);if (it == _msgHandlerMap.end()) // 找不到{// 返回一个默认的处理器,空操作,=按值获取return [=](const TcpConnectionPtr &conn, json &js, Timestamp){LOG_ERROR << "msgid:" << msgid << " can not find handler!"; // muduo日志会自动输出endl};}else // 成功的话{return _msgHandlerMap[msgid]; // 返回这个处理器}
}// 处理登录业务     {"msgid":1,"id":22,"password":"123456"}
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"] = "该账号已经登录,请重新输入新账号";conn->send(response.dump()); // 回调 ,返回json字符串}else{{// 登陆成功,记录用户连接lock_guard<mutex> lock(_connMutex);_userConnMap.insert(make_pair(id, conn));}// 登录成功,更新用户状态信息user.setState("online");_userModel.updateState(user);json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 0;response["errmsg"] = "登录成功!";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;}conn->send(response.dump()); // 回调 ,返回json字符串}}else{// 用户名或者密码错误json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 1;response["errmsg"] = "用户名或者密码错误";conn->send(response.dump()); // 回调 ,返回json字符串}
}// 处理注册业务         {"msgid":3,"name":"Jiao","password":"123456"}
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp time)
{string name = js["name"];    // 获取名字string pwd = js["password"]; // 获取密码User user; // 创建用户对象user.setName(name);user.setPwd(pwd);bool state = _userModel.insert(user); // 新用户的插入if (state)                            // 插入成功{// 注册成功json response;response["msgid"] = REG_MSG_ACK;response["errno"] = 0;response["id"] = user.getId();conn->send(response.dump()); // 回调 ,返回json字符串}else // 插入失败{// 注册失败json response;response["msgid"] = REG_MSG_ACK;response["errno"] = 1;conn->send(response.dump()); // 回调 ,返回json字符串}
}// 处理客户端异常退出
void ChatService::clientCloseException(const TcpConnectionPtr &conn)
{User user;{lock_guard<mutex> lock(_connMutex);for (auto it = _userConnMap.begin(); it != _userConnMap.end(); it++)    //遍历{if (it->second == conn){user.setId(it->first);// 从_userConnMap表中删除用户的连接信息_userConnMap.erase(it);break;}}}// 更新用户的状态信息if (user.getId() != -1){user.setState("offline");_userModel.updateState(user);}
}// 处理点对点聊天消息
void ChatService::oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{int to_id = js["to"].get<int>();                //获取聊天对象id号{lock_guard<mutex> lock(_connMutex);auto it = _userConnMap.find(to_id);         //查看聊天对象是否在线if (it != _userConnMap.end()){// 聊天对象在线,转发消息,服务器主动推动消息给to_id用户it->second->send(js.dump());return;}}// 聊天对象不在线,插入离线消息表中_offlineMsgModel.insert(to_id, js.dump());
}// 服务端异常,业务重置方法
void ChatService::reset()
{// 把所有online用户的状态设置为offline_userModel.resetState();
}// 添加好友业务             {"msgid":6,"id":22,"name":"Jiao","friendid":13}
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{int userid = js["id"].get<int>();               //获取用户idint friendid = js["friendid"].get<int>();       //获取好友id// 存储消息bool state = _friendModel.insert(userid, friendid);if(state){//添加好友成功!json response;response["msgid"] = ADD_FRIENG_MSG_ACK;response["error"] = 0;response["errmsg"]="成功添加好友!";response["id"] = userid;response["friendid"] = friendid;conn->send(response.dump());}else{//添加好友失败json response;response["msgid"] = ADD_FRIENG_MSG_ACK;response["error"] = 1;response["errmsg"]="添加好友失败!";conn->send(response.dump());}
}

2.3功能验证

登录张三账号,id为13的张三要添加id为15的李四为好友

好友表显示:添加成功

张三再次登录时,会返回张三的好友列表

添加好友业务验证成功!

感兴趣的小伙伴一起来试一下吧~

如果有问题还请及时联系我哦,感谢~

三、项目流程

 1、项目环境搭建 

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

2、Json第三方库介绍

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

3、muduo网络库介绍

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

4、MySQL数据库创建

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

5、网络模块与业务模块代码编写

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

6、MySQL模块编写

C++项目——集群聊天服务器项目(六)MySQL模块-CSDN博客

7、Model层设计、注册业务实现

C++项目——集群聊天服务器项目(七)Model层设计、注册业务实现-CSDN博客

8、用户登录业务

C++项目——集群聊天服务器项目(八)用户登录业务-CSDN博客

9、客户端异常退出业务

C++项目——集群聊天服务器项目(九)客户端异常退出业务-CSDN博客

10、点对点聊天业务

C++项目——集群聊天服务器项目(十)点对点聊天业务-CSDN博客

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

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

相关文章

vsCode 刷 leetcode 使用 Cookie 登录

1. 安装插件 打开 vsCode&#xff0c;选择扩展&#xff0c;搜索 leetcode&#xff0c;选择第一个&#xff0c;带有中文力扣字样&#xff0c;安装后重启 2. 切换终端 插件安装成功之后&#xff0c;侧边栏选择 leetcode 菜单&#xff0c;切换终端&#xff0c;选择中文版本&…

海康摄像头插件嵌入iframe时视频播放插件位置问题

参考&#xff1a;https://juejin.cn/post/6857670423971758094 原因&#xff1a;没有按照iframe相对位置计算视频插件位置。 解决&#xff1a; $(window).on(resize, resize);function resize(){// 解决iframe中嵌入海康插件初始化问题:// 1. 获取iframe相比于窗口的偏移量;c…

Flutter仿Boss-2.启动页、引导页

简述 在移动应用开发中&#xff0c;启动页和引导页是用户初次接触应用时的重要组成部分&#xff0c;能够提升用户体验和导航用户了解应用功能。本文将介绍如何使用Flutter实现启动页和引导页&#xff0c;并展示相关代码实现。 启动页 启动页是应用的第一个页面&#xff0c;首…

Jenkins首次安装选择推荐插件时出现”No such plugin cloudbees-folder”解决方案

安装Jenkins成功之后&#xff0c;首次启动Jenkins后台管理&#xff0c;进入到安装插件的步骤&#xff0c;选择"推荐安装"&#xff0c;继续下一步的时候出现错误提示&#xff1a; 出现一个错误 安装过程中出现一个错误&#xff1a;No such plugin&#xff1a;cloudb…

【大数据存储】实验二 HDFS操作实验

实验二 HDFS操作实验 启动Hadoop&#xff0c;执行jps&#xff0c;检查Hadoop相关进程是否启动成功 启动hadoop 执行jps,可以看到名称节点和数据节点&#xff0c;第二名称节点都打开了&#xff0c;则hadoop相关进程启动成功 在本地文件系统“/home”下新建两个文件夹&#xff…

Dapr(一) 基于云原生了解Dapr

(这期先了解Dapr&#xff0c;之后在推出如何搭建Dapr&#xff0c;以及如何使用。) 目录 引言&#xff1a; Service Mesh定义 Service Mesh解决的痛点 Istio介绍 Service Mesh遇到的挑战 分布式应用的需求 Multiple Runtime 理念推导 Dapr 介绍 Dapr 特性 Dapr 核心…

前后台分离nodejs+vue租房信息网站express-94sk3.

本租房管理系统有管理员&#xff0c;租客&#xff0c;屋主三个角色。管理员功能有个人中心&#xff0c;租客管理&#xff0c;屋主管理&#xff0c;房源信息管理&#xff0c;订单信息管理&#xff0c;屋主申诉管理&#xff0c;通知公告管理&#xff0c;留言板管理&#xff0c;系…

Electron的学习

目录 项目初始化可以看官网非常详细根路径创建.vscode文件夹主进程和渲染进程之前的通信ipcRenderer.send和ipcMain.on的使用ipcRenderer.invoke和ipcMain.handle的使用 切换主题模式文件拖放保存消息通知进度展示图标闪烁自定义菜单自定义右键菜单 项目初始化可以看官网非常详…

基于PSO优化的CNN-LSTM-Attention的时间序列回归预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1卷积神经网络&#xff08;CNN&#xff09;在时间序列中的应用 4.2 长短时记忆网络&#xff08;LSTM&#xff09;处理序列依赖关系 4.3 注意力机制&#xff08;Attention&#xff09; 5…

如何将平板或手机作为电脑的外接显示器?

先上官网链接&#xff1a;ExtensoDesk 家里有一台华为平板&#xff0c;自从买回来以后除了看视频外&#xff0c;基本没什么作用&#xff0c;于是想着将其作为我电脑的第二个屏幕&#xff0c;提高我学习办公的效率&#xff0c;废物再次利用。最近了解到华为和小米生态有多屏协同…

FMEA引领智能家居安全革新,打造无忧智能生活新纪元!

在智能家居日益普及的今天&#xff0c;如何确保家居安全成为消费者关注的焦点。本文将探讨如何通过FMEA&#xff08;故障模式与影响分析&#xff09;这一强大的质量管理工具&#xff0c;为智能家居赋能&#xff0c;打造安全无忧的智能生活新体验。 一、FMEA在智能家居领域的应用…

wireshark数据流分析-学习日记day1

参考内容&#xff1a; 网址hxxp://194.55.224[.]9/liuz/5/fre.php描述Loki Bot C2 网址早在 2023-08-15 就被注意到了2023-07-27 记录的 IcedID C2 域&#xff1a; vrondafarih[.]com - HTTP trafficmagiketchinn[.]com - HTTPS trafficmagizanqomo[.]com - HTTPS traffic 网…

【Python从入门到进阶】52、CrawlSpider链接提取器的使用

接上篇《51、电影天堂网站多页面下载实战》 上一篇我们采用Scrapy框架多页面下载的模式来实现电影天堂网站的电影标题及图片抓取。本篇我们来学习基于规则进行跟踪和自动爬取网页数据的“特殊爬虫”CrawlSpider。 一、什么是CrawlSpider&#xff1f; 1、CrawlSpider的概念 Cr…

算法打卡day23

今日任务&#xff1a; 1&#xff09;39. 组合总和 2&#xff09;40.组合总和II 3&#xff09;131.分割回文串 39. 组合总和 题目链接&#xff1a;39. 组合总和 - 力扣&#xff08;LeetCode&#xff09; 给定一个无重复元素的数组 candidates 和一个目标数 target &#xff0c;…

(十)Docker的基本操作

1.1.镜像操作 1.1.1.镜像名称 首先来看下镜像的名称组成&#xff1a; 镜名称一般分两部分组成&#xff1a;[repository]:[tag]。在没有指定tag时&#xff0c;默认是latest&#xff0c;代表最新版本的镜像 如图&#xff1a; 这里的mysql就是repository&#xff0c;5.7就是…

Python学习: 错误和异常

Python 语法错误 解析错误&#xff08;Parsing Error&#xff09;通常指的是程序无法正确地解析&#xff08;识别、分析&#xff09;所给定的代码&#xff0c;通常是由于代码中存在语法错误或者其他无法理解的结构导致的。这可能是由于缺少括号、缩进错误、未关闭的引号或其他括…

数据结构——二叉树——二叉搜索树(Binary Search Tree, BST)

目录 一、98. 验证二叉搜索树 二、96. 不同的二叉搜索树 三、538. 把二叉搜索树转换为累加树 二叉搜索树&#xff1a;对于二叉搜索树中的每个结点&#xff0c;其左子结点的值小于该结点的值&#xff0c;而右子结点的值大于该结点的值 一、98. 验证二叉搜索树 给你一个二叉树的…

P6维护:Oracle P6服务性能优化

前言 本文将介绍如何对ORACLE Primavera P6 EPPM软件进行性能调优&#xff0c;考虑到P6主要采用JAVA语言编制&#xff0c;且其使用的是Weblogic Server应用服务器部署P6各项服务器&#xff0c;其性能优化的原理便是基于其JVM特征参数进行设置 方法一&#xff1a;修改配置文件…

Maplesoft Maple 2024(数学科学计算)mac/win

Maplesoft Maple是一款强大的数学计算软件&#xff0c;提供了丰富的功能和工具&#xff0c;用于数学建模、符号计算、数据可视化等领域的数学分析和解决方案。 Mac版软件下载&#xff1a;Maplesoft Maple 2024 for mac激活版 WIn版软件下载&#xff1a;Maplesoft Maple 2024特别…

银行业架构网络BIAN (Banking IndustryArchitecture Network)详细介绍

BIAN ( The Banking Industry Architecture Network) 是一个业界多方协作的非营利性组织&#xff0c;由全球领先银行、技术提供商、顾问和学者组成&#xff0c;定义了一个用以简化和标准化核心银行体系结构的银行技术框架。这一框架基于面向服务的架构 (SOA) 原则&#xff0c;银…