嘉兴做网站建设的公司/制作网页模板
跟着施磊老师做C++项目,施磊老师_腾讯课堂 (qq.com)
本文在此篇博客的基础上继续实现数据模块和业务模块代码:
C++集群聊天服务器 网络模块+业务模块+CMake构建项目 笔记 (上)-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/135991635?spm=1001.2014.3001.5501一、mysql 项目数据库和表的设计
myql 项目数据库和表的设计-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/135981407?spm=1001.2014.3001.5501二、mysql数据库代码封装
- include/public.hpp
#ifndef PUBLIC_H
#define PUBLIC_H
/*server和client的公共文件
*/
enum EnMsgType {LOGIN_MSG = 1, // 登录消息LOGIN_MSG_ACK, // 登录响应消息REG_MSG, // 注册消息REG_MSG_ACK // 注册响应消息
};
#endif // PUBLIC_H
- include/server/db/db.h
#ifndef DB_H
#define DB_H#include <mysql/mysql.h>
#include <string>
using namespace std;// 数据库操作类
class Mysql {
public:// 初始化数据库连接Mysql();// 释放数据库连接资源~Mysql();// 连接数据库bool connect();// 更新操作bool update(string sql);// 查询操作MYSQL_RES *query(string sql);// 获取连接MYSQL *getConnection();
private:MYSQL *m_conn;
};#endif // DB_H
src/server/db/db.cpp
#include "db.h"
#include <muduo/base/Logging.h>
// 数据库配置信息
static string server = "127.0.0.1";
static string user = "root";
static string password = "123456";
static string dbname = "chat";// 初始化数据库连接
Mysql::Mysql() {m_conn = mysql_init(nullptr);// 这里相当于只是给它开辟了一块存储连接数据的资源空间
}// 释放数据库连接资源
Mysql::~Mysql() {if(m_conn != nullptr) {mysql_close(m_conn);}// 析构的时候把这块资源空间用mysql_close掉
}// 连接数据库
bool Mysql::connect() {MYSQL *p = mysql_real_connect(m_conn,server.c_str(),user.c_str(),password.c_str(),dbname.c_str(),3306,nullptr,0);if(p!=nullptr) {// C和C++代码默认的编码字符是ASCII,如果不设置,// 从MYSQL上拉下来的中文显示?mysql_query(m_conn, "set names gbk");LOG_INFO << "connect mysql success!!!";} else{LOG_INFO << "connect mysql failed!!!";}return p;
}// 更新操作
bool Mysql::update(string sql) {if(mysql_query(m_conn, sql.c_str())) {LOG_INFO << __FILE__ << ":" << __LINE__ << ":" << sql <<"更新失败!";return false;}return true;
}// 查询操作
MYSQL_RES* Mysql::query(string sql) {if(mysql_query(m_conn, sql.c_str())) {LOG_INFO << __FILE__ << ":" << __LINE__ << ":"<< sql <<"查询失败!"; return nullptr;}return mysql_use_result(m_conn);
}// 获取连接
MYSQL* Mysql::getConnection() {return m_conn;
}
三、Model数据层代码框架设计
- include/server/user.hpp
#ifndef USER_H
#define USER_H#include <string>
using namespace std;// 匹配User表的ORM类
class User {
public:User(int id=-1, string name="", string password="", string state="offline") {m_id = id;m_name = name;m_password = password;m_state = state;}void setId(int id) { m_id = id; }void setName(string name) { m_name = name; }void setPwd(string pwd) { m_password = pwd; } void setState(string state) { m_state = state; }int getId() const { return m_id; }string getName() const { return m_name; }string getPwd() const { return m_password; }string getState() const { return m_state; }
private:int m_id;string m_name;string m_password;string m_state;
};
#endif // USER_H
- include/server/usermodel.hpp
#ifndef USERMODEL_H
#define USERMODEL_H
#include "user.hpp"
// User表的数据操作类
class UserModel {
public:// user表的增加方法bool insert(User& user); // 根据用户号码查询用户信息User query(int id);// 更新用户的状态信息bool updateState(User user);
};#endif // USERMODEL_H
- src/server/usermodel.cpp
#include "usermodel.hpp"
#include "db.h"
#include <iostream>
// User表的增加方法
bool UserModel::insert(User &user) {// 1.组装sql语句char sql[1024] = {0};std::sprintf(sql,"insert into user(name,password,state) values('%s','%s', '%s')",user.getName().c_str(), user.getPwd().c_str(), user.getState().c_str());// 2.执行sql语句Mysql mysql;if(mysql.connect()) {if(mysql.update(sql)) {// 获取插入成功的用户数据生成的主键iduser.setId(mysql_insert_id(mysql.getConnection()));return true;}}return false;
}// 根据用户号码查询用户信息
User UserModel::query(int id) {// 1.组装sql语句char sql[1024] = {0};sprintf(sql,"select * from user where id = %d", id);// 2.执行sql语句Mysql mysql;if(mysql.connect()) {MYSQL_RES* res = mysql.query(sql);if(res != nullptr) {MYSQL_ROW row = mysql_fetch_row(res);if(row != nullptr) {User user;user.setId(atoi(row[0]));user.setName(row[1]);user.setPwd(row[2]);user.setState(row[3]);// 释放资源mysql_free_result(res);return user;}}}return User();
}// 更新用户的状态信息
bool UserModel::updateState(User user) {// 1.组装sql语句char sql[1024] = {0};sprintf(sql,"update user set state = '%s' where id = %d",user.getState().c_str(), user.getId());// 2.执行sql语句Mysql mysql;if(mysql.connect()) {if(mysql.update(sql)) {return true;}}return false;
}
四、CMake 构建项目
- src/server/CMakeLists.txt
# 定义了一个SRC_LIST变量 包含了该目录下所有的源文件
aux_source_directory(. SRC_LIST)
aux_source_directory(./db DB_LIST)# 指定生成可执行文件
add_executable(ChatServer ${SRC_LIST} ${DB_LIST})# 指定可执行文件链接时需要依赖的库文件
target_link_libraries(ChatServer muduo_net muduo_base mysqlclient pthread)
- src/CMakeLists.txt
add_subdirectory(server)
- 和src,include,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}/include/server/db)
include_directories(${PROJECT_SOURCE_DIR}/thirdparty)# 加载子目录
add_subdirectory(src)
cmake -B build
cmake --build build
1.测试注册:
telnet 127.0.0.1 6000
{"msgid":3,"name":"heheda","password":"1024"} // 注册
{"msgid":3,"name":"Tom","password":"520"} // 注册
{"msgid":3,"name":"Jerry","password":"1314"} // 注册
2.测试登录:
(1)未登录
(2) 已经登录
telnet 127.0.0.1 6000
{"msgid":1,"id":4,"password":"1024"}
telnet 127.0.0.1 6000
{"msgid":1,"id":4,"password":"1024"}
(3)登录失败
3.gdb排错练习
比如输入以下这句,其实"id":5才对,但是如果误输入的会引起核心中断,如何排查错误呢?
{"msgid":1,"id":"5","password":"520"}
>>gdb调试,比如我们怀疑可能是chatservice.cpp的20行出错了
heheda@linux:~/Linux/Server$ gdb ./bin/ChatServer
(gdb) break chatservice.cpp 20
(gdb) run
telnet 127.0.0.1 6000
输入:
{"msgid":1,"id":"5","password":"520"}
检查出错误了:
reason: [json.exception.type_error.302] type must be number, but is string
故我们把
{"msgid":1,"id":"5","password":"520"}修改为以下:
{"msgid":1,"id":5,"password":"520"}
总结:客户端发送过来一个注册的业务,先从最开始的网络,再通过事件的分发,到业务层的相关的handler处理注册,接着访问底层的model。其中在业务类设计,这里看到的都是对象,方便你把底层的数据模块改成你想要的,例如mysql,sql,oracle,mongoDB等都行。实现了网络模块,业务模块以及数据模块的低耦合。
五、记录用户的连接信息以及线程安全问题
- 在ChatService.hpp文件中,private处添加
private:// 存储在线用户的通信连接unordered_map<int,TcpConnectionPtr> m_userConnMap;// 定义互斥锁,保证m_userConnMap的线程安全mutex m_connMutex;
- 修改ChatService.cpp中的login函数,在登录成功,记录用户连接信息,将id和conn数据信息插入m_userConnMap,使用lock_guard使得线程安全
// 处理登录业务 user表:id password
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time) {int id = js["id"].get<int>();string pwd = js["password"];User user = m_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());}else{// 登录成功,记录用户连接信息{lock_guard<mutex> lock(m_connMutex);m_userConnMap.insert({id, conn});}// 登录成功,更新用户状态信息 state: offline => onlineuser.setState("online");m_userModel.updateState(user);json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 0;response["id"] = user.getId();response["name"] = user.getName();conn->send(response.dump());}}else {// 该用户不存在/用户存在但是密码错误,登录失败json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 1;response["errmsg"] = "该用户不存在,您输入用户名或者密码可能错误!";conn->send(response.dump());}
}
六、客户端异常退出业务代码和测试
- 在ChatService.hpp中添加处理客户端异常退出的函数声明
public:// 处理客户端异常退出void clientCloseException(const TcpConnectionPtr& conn);
- 在ChatService.cpp中编写处理客户端异常退出的函数
// 处理客户端异常退出
void ChatService::clientCloseException(const TcpConnectionPtr &conn) {User user;{lock_guard<mutex> lock(m_connMutex); for(auto it = m_userConnMap.begin();it!=m_userConnMap.end();++it) {if(it->second == conn) {// 从map表删除用户的链接信息user.setId(it->first);m_userConnMap.erase(it);break;}}}// 更新用户的状态信息if(user.getId() != -1) {user.setState("offline");m_userModel.updateState(user);}}
表里原先有Tom登录用户的信息,然后我们登录了该账号,就从offline状态更新为online状态
按下ctrl+],切换到telnet>,输入quit,此时客户端异常退出,也就执行了从online更新为offline
七、离线消息业务代码实现和测试
- 如果用户登录成功的话,查询该用户是否有离线消息,desc offlinemessage
- offlinemessagemodel.hpp
#ifndef OFFLINEMESSAGEMODEL_H
#define OFFLINEMESSAGEMODEL_H
#include <string>
#include <vector>
using namespace std;// 提供离线消息表的操作接口方法
class OfflineMsgModel {
public:// 存储用户的离线消息void insert(int userid, string msg);// 删除用户的离线消息void remove(int userid);// 查询用户的离线消息vector<string> query(int userid);
};#endif // OFFLINEMESSAGEMODEL_H
- offlinemessagemodel.cpp
#include "offlinemessagemodel.hpp"
#include "db.h"
// 存储用户的离线消息
void OfflineMsgModel::insert(int userid, string msg) {// 1.组装sql语句char sql[1024] = {0};sprintf(sql, "insert into offlinemessage values(%d, '%s')", userid, msg.c_str());// 2.执行sql语句Mysql mysql;if(mysql.connect()) {mysql.update(sql);}
}// 删除用户的离线消息
void OfflineMsgModel::remove(int userid) {// 1.组装sql语句char sql[1024] = {0};sprintf(sql, "delete from offlinemessage where userid = %d", userid);// 2.执行sql语句Mysql mysql;if(mysql.connect()) {mysql.update(sql);}
}// 查询用户的离线消息
vector<string> OfflineMsgModel::query(int userid) {// 1.组装sql语句char sql[1024] = {0};sprintf(sql, "select message from offlinemessage where userid = %d", userid);// 2.执行sql语句Mysql mysql;vector<string> vec;if(mysql.connect()) {MYSQL_RES *res = mysql.query(sql);if(res != nullptr) {// 把userid用户的所有离线消息放入vec中返回MYSQL_ROW row;while((row = mysql_fetch_row(res)) != nullptr) {vec.push_back(row[0]);}mysql_free_result(res);return vec;}}return vec;
}
- 在chatservice.hpp中添加
#include "offlinemessagemodel.hpp"// 聊天服务器业务类
class ChatService {
private: OfflineMsgModel m_offlineMsgModel;
}
- chatservice.cpp
// 处理登录业务 user表:id password
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time) {int id = js["id"].get<int>();string pwd = js["password"];User user = m_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());}else{// 登录成功,记录用户连接信息{lock_guard<mutex> lock(m_connMutex);m_userConnMap.insert({id, conn});}// 登录成功,更新用户状态信息 state: offline => onlineuser.setState("online");m_userModel.updateState(user);json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 0;response["id"] = user.getId();response["name"] = user.getName();// 查询该用户是否有离线消息vector<string> vec = m_offlineMsgModel.query(id);if(!vec.empty()) {response["offlinemsg"] = vec;// 读取该用户的离线消息后,把该用户的所有离线消息删除掉m_offlineMsgModel.remove(id);}conn->send(response.dump());}}else {// 该用户不存在/用户存在但是密码错误,登录失败json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 1;response["errmsg"] = "该用户不存在,您输入用户名或者密码可能错误!";conn->send(response.dump());}
}// 一对一聊天业务
void ChatService::oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time) {int toid = js["to"].get<int>();{lock_guard<mutex> lock(m_connMutex);auto it = m_userConnMap.find(toid);if(it != m_userConnMap.end()) {// toid在线,转发消息 服务器主动推送消息给toid用户it->second->send(js.dump());return;}}// toid不在线,存储离线消息m_offlineMsgModel.insert(toid, js.dump());
}
八、服务器异常退出处理代码和测试
- main.cpp
#include "chatserver.hpp"
#include "chatservice.hpp"
#include <iostream>
#include <signal.h>
using namespace std;// 处理服务器ctrl+c结束后,重置user的状态信息
void resetHandler(int) {ChatService::getInstance()->reset();exit(0);
}int main() {signal(SIGINT,resetHandler);...
}
- 在chatservice.hpp添加reset()方法声明,服务器异常,业务重置方法
// 服务器异常,业务重置方法
void reset();
- 在chatservice.cpp中编写reset()方法
// 服务器异常,业务重置方法
void ChatService::reset() {// 把online状态的用户,设置成offlinem_userModel.resetState();
}
- 在usermodel.hpp中添加重置用户的状态信息resetState方法声明
// 重置用户的状态信息
void resetState();
- 在usermodel.cpp中编写resetState()方法
// 重置用户的状态信息
void UserModel::resetState() {// 1.组装sql语句char sql[1024] = "update user set state = 'offline' where state = 'online'";// 2.执行sql语句Mysql mysql;if(mysql.connect()) {mysql.update(sql);}
}
- ctrl+c终止服务
九、添加好友业务代码和测试
- public.hpp
#ifndef PUBLIC_H
#define PUBLIC_H
/*server和client的公共文件
*/
enum EnMsgType {LOGIN_MSG = 1, // 登录消息LOGIN_MSG_ACK, // 登录响应消息REG_MSG, // 注册消息REG_MSG_ACK, // 注册响应消息ONE_CHAT_MSG, // 聊天消息ADD_FRIEND_MSG, // 添加好友消息
};
#endif // PUBLIC_H
- friendmodel.hpp
#ifndef FRIENDMODEL_H
#define FRIENDMODEL_H#include "user.hpp"
#include <vector>
using namespace std;// 维护好友信息的操作接口方法
class FriendModel {
public:// 添加好友关系void insert(int userid, int friendid);// 返回用户好友列表 friendid vector<User> query(int userid);
};#endif // FRIENDMODEL_H
- friendmodel.cpp
#include "friendmodel.hpp"
#include "db.h"
// 添加好友关系
void FriendModel::insert(int userid, int friendid) {// 1.组装sql语句char sql[1024] = {0};sprintf(sql, "insert into friend values (%d, %d)", userid, friendid);// 2.执行sql语句Mysql mysql;if(mysql.connect()) {mysql.update(sql);}
}
// 返回用户好友列表 friendid
vector<User> FriendModel::query(int userid) {// 1.组装sql语句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); vector<User> vec;Mysql mysql;if(mysql.connect()) {MYSQL_RES * res = mysql.query(sql);if(res != nullptr) {// 把userid用户的所有离线消息放入vec中返回MYSQL_ROW row;while((row = mysql_fetch_row(res)) != nullptr) {User user;user.setId(atoi(row[0])); // iduser.setName(row[1]); // nameuser.setState(row[2]); // statevec.push_back(user);}mysql_free_result(res); // 释放资源return vec;}}return vec;
}// select a.id,a.name,a.state from user a inner join
// friend b on b.friendid = a.id
// where b.userid = %d
- chatservice.hpp
// 聊天服务器业务类
class ChatService {
public:// 添加好友业务void addFriend(const TcpConnectionPtr& conn,json& js,Timestamp time);
private:FriendModel m_friendModel;
}
- chatservice.cpp
// 注册消息以及对应的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)}); m_msgHandlerMap.insert({ONE_CHAT_MSG,std::bind(&ChatService::oneChat, this, _1, _2, _3)});m_msgHandlerMap.insert({ADD_FRIEND_MSG,std::bind(&ChatService::addFriend, this, _1, _2, _3)});
}// 处理登录业务 user表:id password
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time) {int id = js["id"].get<int>();string pwd = js["password"];User user = m_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());}else{// 登录成功,记录用户连接信息{lock_guard<mutex> lock(m_connMutex);m_userConnMap.insert({id, conn});}// 登录成功,更新用户状态信息 state: offline => onlineuser.setState("online");m_userModel.updateState(user);json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 0;response["id"] = user.getId();response["name"] = user.getName();// 查询该用户是否有离线消息vector<string> vec = m_offlineMsgModel.query(id);if(!vec.empty()) {response["offlinemsg"] = vec;// 读取该用户的离线消息后,把该用户的所有离线消息删除掉m_offlineMsgModel.remove(id);}// 查询该用户的好友信息并返回vector<User>userVec = m_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());}}else {// 该用户不存在/用户存在但是密码错误,登录失败json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 1;response["errmsg"] = "该用户不存在,您输入用户名或者密码可能错误!";conn->send(response.dump());}
}// 添加好友业务 msgid id friendid
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time) {int userid = js["id"].get<int>();int friendid = js["friendid"].get<int>();// 存储好友信息m_friendModel.insert(userid, friendid);
}
十、模拟QQ好友添加(呵呵哒改造)
- public.hpp
#ifndef PUBLIC_H
#define PUBLIC_H
/*server和client的公共文件
*/
enum EnMsgType {LOGIN_MSG = 1, // 登录消息LOGIN_MSG_ACK, // 登录响应消息REG_MSG, // 注册消息REG_MSG_ACK, // 注册响应消息ONE_CHAT_MSG, // 聊天消息ADD_FRIEND_REQ_MSG, // 添加好友请求消息ADD_FRIEND_MSG_ACK, // 添加好友响应消息
};
#endif // PUBLIC_H
- chatservice.hpp
// 聊天服务器业务类
class ChatService {
public:// 添加好友业务请求void addFriendRequest(const TcpConnectionPtr& conn,json& js,Timestamp time);// 添加好友业务响应void addFriendResponse(const TcpConnectionPtr& conn,json& js,Timestamp time);
}
- chatservice.cpp
// 注册消息以及对应的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)}); m_msgHandlerMap.insert({ONE_CHAT_MSG,std::bind(&ChatService::oneChat, this, _1, _2, _3)});m_msgHandlerMap.insert({ADD_FRIEND_REQ_MSG,std::bind(&ChatService::addFriendRequest, this, _1, _2, _3)}); m_msgHandlerMap.insert({ADD_FRIEND_MSG_ACK,std::bind(&ChatService::addFriendResponse, this, _1, _2, _3)});
}// 添加好友业务请求
void ChatService::addFriendRequest(const TcpConnectionPtr &conn, json &js, Timestamp time) {int userid = js["id"].get<int>();int friendid = js["friendid"].get<int>();json response;response["msgid"] = ADD_FRIEND_REQ_MSG;response["msg"] = "Please add me as a friend, thank you!";response["from"] = userid;response["to"] = friendid;// std::cout<<"来到这里了"<<std::endl;oneChat(conn,response,time);
}// 添加好友业务 msgid id friendid
void ChatService::addFriendResponse(const TcpConnectionPtr &conn, json &js, Timestamp time) {int userid = js["id"].get<int>();int friendid = js["friendid"].get<int>();bool flag = js["flag"].get<bool>();json response;response["msgid"] = ADD_FRIEND_MSG_ACK;response["from"] = userid;response["to"] = friendid;if(flag) {response["msg"] = "I very happy to make friends with you!!!";m_friendModel.insert(userid, friendid);}else{response["msg"] = "I am very sorry, you are not my friend!!!";}oneChat(conn,response,time);
}
情景一:id=1的用户 想要和 id=2的在线用户交个朋友,向其发送好友请求,id=2的在线用户响应同意互为好友
先登录两个账号:
{"msgid":1,"id":1,"password":"1024"} // 登录
{"msgid":1,"id":2,"password":"520"} // 登录
(1)id=1的用户,发送好友请求
{"msgid":6,"id":1,"friendid":2} // 发送好友请求
(2)响应好友请求(允许)
{"msgid":7,"id":2,"friendid":1,"flag":true} // 响应好友请求(允许)
情景二:id=1的用户 想要和 id=3的离线用户交个朋友,向其发送好友请求,id=2的离线用户登录后,在线时看到离线消息,响应不同意互为好友
(1)发送好友请求
先登录一个账号,id=1的用户:
{"msgid":1,"id":1,"password":"1024"} // 登录
(1)发送好友请求
{"msgid":6,"id":1,"friendid":3} // 发送好友请求
(2)响应好友请求(拒绝)
再登录id=3的用户账号,查看到来自id=1用户发来的离线消息
{"msgid":1,"id":3,"password":"1314"} // 登录
id=3的用户,响应好友请求(拒绝)
{"msgid":7,"id":3,"friendid":1,"flag":false} // 响应好友请求(拒绝)
相关文章:

C++集群聊天服务器 数据模块+业务模块+CMake构建项目 笔记 (上)
跟着施磊老师做C项目,施磊老师_腾讯课堂 (qq.com) 本文在此篇博客的基础上继续实现数据模块和业务模块代码: C集群聊天服务器 网络模块业务模块CMake构建项目 笔记 (上)-CSDN博客https://blog.csdn.net/weixin_41987016/article…...

#Js篇:字符串的使用方法es5和es6
字符串 \ :单引号(\u0027)\" :双引号(\u0022) charAt 定义: 返回指定位置的字符,参数时从0开始编号的位置 参数: 位置下标 abc.charAt(1) // "b" …...

SpringBoo+Vue构建简洁日志文件查看系统
点击下载《SpringBooVue构建日志文件查看系统(源代码)》 1. 前言 想必经常做java开发的小伙伴,其大多数服务都是运行在linux系统上的,当遇到一些比较棘手的bug需要处理时,经常要上服务器去捞日志,然后通过…...

JavaScript基础第二天
JavaScript基础第二天 今天我们学习if分支语句、三元表达式和switch-case语句。 1. if分支语句 1.1 语法 if (条件表达式){// 满足条件要执行的语句 } else {// 不满足条件要执行的语句 }if中的内容如果为true,就执行大括号的代码块,如果为false执行…...

2、卷积和ReLU激活函数
python了解集合网络如何创建具有卷积层的特性。 文章目录 简介特征提取(Feature Extraction)卷积过滤(Filter with Convolution)Weights(权重)激活(Activations)用ReLU检测示例 - 应用卷积和ReLU结论In [1]: import numpy as np from itertools import productdef show_kerne…...

SQL世界之基础命令语句
目录 一、SQL SELECT 语句 1.SQL SELECT 语法 2.SQL SELECT 实例 3.SQL SELECT * 实例 二、SQL SELECT DISTINCT 语句 1.语法 2.使用 DISTINCT 关键词 三、SQL SELECT WHERE 语句 1.WHERE 子句 2.语法 3.使用 WHERE 子句 4.引号的使用 四、SQL SELECT AND&OR …...

Facebook未来展望:社交媒体的下一个篇章
社交媒体一直是连接人与人之间的纽带,而Facebook则一直在推动这一领域的发展。随着科技不断演进和社会需求的不断变迁,Facebook正积极筹谋社交媒体的下一个篇章。本文将深入剖析Facebook的未来展望,探讨其在社交媒体领域所迎接的新时代。 1. …...

源码搭建教学:直播带货商城小程序开发
结合小程序开发的直播带货商城,不仅可以提供更便捷的购物体验,还可以实现更高效的销售。因此,学习如何搭建一个直播带货商城小程序将成为您拓展商业领域的利器。 步骤一:准备工作 在开始开发之前,您需要进行一些准备工…...

vue-cli引入本地json数据:封装为js文件,无需请求直接读取
vue-cli引入本地json数据 1、新建js文件(路径自定义),写入JSON数据 /* jsonData.js */export let jsonData { // 声明变量,存储数据// JSON源数据 }2、组件内引入js文件,读取数据 /* Example.vue */import { json…...

20240202在Ubuntu20.04.6下使用whisper.cpp的显卡模式
20240202在Ubuntu20.04.6下使用whisper.cpp的显卡模式 2024/2/2 19:43 【结论:在Ubuntu20.04.6下,确认large模式识别7分钟中文视频,需要356447.78 ms,也就是356.5秒,需要大概5分钟!效率太差!】 …...

前端面试拼图-数据结构与算法
摘要:总结一些前端算法题,持续更新! 一、数据结构与算法 时间复杂度-程序执行时需要的计算量(CPU) 空间复杂度-程序执行时需要的内存空间 前端开发:重时间,轻空间 1.把一个数组旋转k步 arr…...

在C++的union中使用std::string(非POD对象)的陷阱
struct和union的对比 union最开始是C语言中的关键字,在嵌入式中比较常见,由于嵌入式内存比较稀缺,所以常用union用来节约空间,在其他需要节省内存的地方也可以用到这个关键字,写一个简单程序来说明union的用途 struc…...

Spring Cloud Netflix Eureka的参数调优
下面主要分为Client端和Server端两大类进行简述,Eureka的几个核心参数 客户端参数 Client端的核心参数 参数默认值说明eureka.client.availability-zones告知Client有哪些region以及availability-zones,支持配置修改运行时生效eureka.client.filter-o…...

Wireshark不显示Thrift协议
使用Wireshark对thrift协议进行抓包,但是只显示了传输层的tcp协议: "右键" -> "Decode As" 选择thrift的tcp端口 将“当前”修改为Thrift,然后点击“确定” 设置后,可以发现Wireshark里面显示的协议从Tcp变…...

VMware虚拟机安装openEuler系统(一)(2024)
目录 一、下载ISO镜像 二、开始创建虚拟机 通过实践是学习openEuler开源Linux系统的最佳方式。因此我们首先得搭建一个openEuler实战环境,文章是在Windows系统上使用VMware Workstation虚拟化软件,安装和学习openEuler开源Linux操作系统。 使用虚拟机…...

Rust入门
文章目录 一、HelloWorld二、控制台输入 以最简单的两个Rust程序例子入门Rust。首先需要下载安装Rust,之后在VSCode或Clion中运行Rust需要下载Rust插件 一、HelloWorld fn main(){println!("Hello World!"); }二、控制台输入 use std::io::stdin; fn …...

RabiitMQ延迟队列(死信交换机)
Dead Letter Exchange(死信交换机) 在MQ中,当消息成为死信(Dead message 死掉的信息)后,消息中间件可以将其从当前队列发送到另一个队列中,这个队列就是死信队列。而 在RabbitMQ中,由…...

浅谈应该遵守的伦敦银交易规则
做伦敦银投资的朋友应遵守伦敦银交易规则,伦敦银交易规则不是指那些伦敦银交易技巧,而是在这个市场中要遵循的一些约定,下面我们就来讨论一下。 风险管理。风险管理即指投资者控制自己一笔乃至整体交易的风险,没有风险管理意识的投…...

安装opencart
一、安装模板 Install SO Emarket Opencart 4 Theme 一:so_emarket_quick2 二:theme package installation 1、installed opencart Default 2、Extensions->Installer->Upload->so_emarket_theme_oc4011_home21_to_home35_v2.0.3->so_theme…...

Qt PCL学习(一):环境搭建
参考 (QT配置pcl)PCL1.12.1QT5.15.2vs2019cmake3.22.4vtk9.1.0visual studio2019Qt5.15.2PCL1.12.1vtk9.1.0cmake3.22.2 本博客用到的所有资源 版本一览:Visual Studio 2019 Qt 5.15.2 PCL 1.12.1 VTK 9.1.0https://pan.baidu.com/s/1xW7xCdR5QzgS1_d1NeIZpQ?pw…...

代码随想录算法训练营第四十二天 | 416. 分割等和子集
题目链接:416. 分割等和子集 文章讲解:代码随想录 416. 分割等和子集讲解 视频讲解:动态规划之背包问题,这个包能装满吗?| LeetCode:416.分割等和子集 思路和解法 题目: 给你一个 只包含正整…...

Spring GateWay
概述简介 能干什么 反向代理 鉴权 流量控制 熔断 日志监控 Spring Cloud Gateway 与Zuul的区别 在SpringCloud Finchley正式版之前,Spring Cloud推荐的网关是 Netflix提供的Zuul: 1、Zuul 1.x,是一个基于阻塞Ⅳ/O的APl Gateway 2、Zuul 1.x基于Servl…...

介绍一个关于 JSON 可视化的网站
最近在看到一个比较好玩的网站,可以将 JSON以可视化的方式展现出现,比如存在一下JSON数据: {"id": "f3bbc3bc-9f34-4bf7-8a0f-7e6f6e6fbb9a","isActive": false,"age": 25,"name": "…...

系统架构设计师-22年-上午答案
系统架构设计师-22年-上午答案 更多软考资料 https://ruankao.blog.csdn.net/ 1 ~ 10 1 云计算服务体系结构如下图所示,图中①、②、③分别与 SaaS PaaS Iaas相对应,图中①、②、③应为(1) #mermaid-svg-xqMbIVMC8pWrne2L {font-family:"trebuch…...

2024 年改变行业的人工智能主要趋势
1、导读 当我们迈入 2024 年时,了解人工智能趋势至关重要。它们不仅仅涉及技术进步;还涉及技术进步。它们意味着我们解决问题、做出决策和展望未来的方式发生了转变。本文旨在探索这些变革趋势,并强调人工智能如何不断突破可能性的界限&…...

【Linux Day15 TCP网络通讯】
TCP网络通讯 TCP编程流程 接口介绍 socket()方法是用来创建一个套接字,有了套接字就可以通过网络进行数据的收发。创建套接字时要指定使用的服务类型,使用 TCP 协议选择流式服务(SOCK_STREAM)。 **bind()方法是用来指定套接字使…...

力扣:78. 子集
回溯解法思路: 1.跟前面的组合题目有相同的点,主要区别在于:组合题目是遍历到符合条件的组合时加入li1集合中,子集题目是每递归一次就要把结果加入到li1集合中,并遍历但nums数组的最后。其他点和组合问题一样。 clas…...

(29)数组异或操作
文章目录 每日一言题目解题思路方法一方法二 代码方法一方法二 结语 每日一言 泉涸,鱼相与处于陆,相呴以湿,相濡以沫,不如相忘于江湖。 --庄子内篇大宗师 题目 题目链接:数组异或操作 给你两个整数,n 和…...

mac检查CPU温度和风扇速度软件:Macs Fan Control Pro 1.5.17中文版
Macs Fan Control Pro for Mac是一款专业的电脑风扇控制工具,旨在帮助Mac用户有效控制电脑的风扇速度,提高电脑的运行效率和稳定性。 软件下载:Macs Fan Control Pro 1.5.17中文版 该软件支持多种风扇控制模式和预设方案,用户可以…...

数据结构——单链表详解
目录 前言 一.什么是链表 1.概念 编辑 2.分类 二.单链表的实现(不带头单向不循环链表) 2.1初始化 2.2打印 2.3创建新节点 2.4头插、尾插 2.5头删、尾删 2.6查找 2.7在指定位置之前插入 2.8在指定位置之后插入 2.9删除pos位置 2.10删除pos之后的 2.11销毁链表…...