集群聊天服務器各個類進行詳解

1.dh.h

類定義概要

類名:?MySQL

功能:?簡化MySQL的連接、查詢和更新操作,提供接口給上層應用使用。


成員變量

private:MYSQL *_conn;
  • _conn:指向MYSQL結構體的指針,用于代表數據庫連接實例。由mysql_init()初始化,之后通過mysql_real_connect建立連接。

構造與析構函數

MySQL()

  • 作用:?初始化數據庫連接對象,調用mysql_init(nullptr)
  • 功能:?為連接準備一個空的MYSQL實例。

~MySQL()

  • 作用:?釋放數據庫連接資源。
  • 調用:?mysql_close(_conn),關閉連接,釋放資源。

成員函數(公共接口)

bool connect()

  • 用途:?建立實際的數據庫連接。
  • 實現細節:
    • 調用mysql_real_connect,連接到配置信息中的數據庫(serveruserpassworddbname、端口3306)。
    • 連接成功后,設置字符集為gbkmysql_query(_conn, "set names gbk"))。
    • 連接成功則輸出日志“connect mysql success!”。
    • 連接失敗則輸出詳細的錯誤信息,包含錯誤原因和連接參數。
  • 返回值:
    • true:連接成功
    • false:連接失敗

bool update(string sql)

  • 用途:?執行SQL更新操作(INSERT、UPDATE、DELETE等)。
  • 實現細節:
    • 調用mysql_query(_conn, sql.c_str())執行SQL。
    • 若失敗,輸出錯誤,返回false
    • 成功返回true
  • 示例:?插入新用戶或修改數據。

MYSQL_RES *query(string sql)

  • 用途:?執行SQL查詢語句(SELECT),返回結果集。
  • 實現細節:
    • 調用mysql_query(_conn, sql.c_str())執行查詢。
    • 若失敗,輸出錯誤信息,返回nullptr
    • 若成功,調用mysql_use_result(_conn)獲取結果集。
  • 返回值:
    • MYSQL_RES*:結果集指針,調用者需負責釋放。

MYSQL* getConnection()

  • 用途:?提供底層連接指針,便于更多的低級操作或調試。
  • 返回:?_conn指針。

其他細節

  • 連接信息存儲:

    • server127.0.0.1
    • userroot
    • passwordSf523416&111
    • dbnamechat

    這些信息是靜態變量(static string),方便在整個程序中使用。

  • 日志輸出:

    • 使用?muduo/base/Logging.h?提供的日志宏?LOG_INFO?和?LOG_ERROR,方便調試和監控。

總結

這個?MySQL?類封裝了:

  • 連接管理:?內部維護一個MYSQL*連接句柄。
  • 連接方法:?connect(),連接數據庫。
  • 數據操作:
    • update()?執行寫操作(增刪改)。
    • query()?執行讀操作,返回結果集指針。
  • 資源管理:?在析構函數中釋放連接。

它為上層應用提供了簡潔、封裝的數據庫訪問接口,并加入了錯誤信息輸出和日志記錄,非常適合在實際的C++服務器項目中使用。

2.FriendModel

讓我們一起來詳細分析這個FriendModel類,它主要負責維護和處理好友關系相關的操作。這個類封裝了操作好友信息的兩個核心功能:添加好友關系和查詢好友列表。


類定義概述

#ifndef FRIENDMODEL_H
#define FRIENDMODEL_H#include "user.hpp"
#include <vector>
using namespace std;class FriendModel
{
public:// 添加好友關系void insert(int userid, int friendid);// 返回用戶好友列表vector<User> query(int userid);
};#endif

成員方法概述

1.?void insert(int userid, int friendid)
  • 功能:?在好友關系表中插入一條新的好友關系。
2.?vector<User> query(int userid)
  • 功能:?查詢某個用戶的所有好友,返回一個User對象的向量(好友列表)。

方法詳細解讀

1. 添加好友關系:void insert(int userid, int friendid)

實現邏輯:

  • 構造SQL語句,格式為:
    insert into friend values(userid, friendid)
    
  • 創建MySQL對象,并連接數據庫。
  • 如果連接成功,執行update()方法插入好友關系。

說明:

這個操作假設friend表有兩列(例如:userid?和?friendid),代表一對好友關系。


2. 查詢好友列表:vector<User> query(int userid)

實現邏輯:

  • 構造SQL語句,使用內部連接(inner join),查詢用戶的好友信息:

    select a.id, a.name, a.state
    from user a
    inner join friend b on b.friendid = a.id
    where b.userid = userid
    

    這條語句的作用:

    • user表中選出所有該用戶的好友信息。
    • 通過friend關系表關聯用戶ID。
  • 創建一個vector<User>存放好友信息。

  • 建立數據庫連接,執行查詢:

    • 若成功,遍歷MYSQL_RES結果集:

      • 利用mysql_fetch_row()讀取每一行數據。

      • 創建一個User對象,設置其字段(idnamestate)。

      • User加入結果向量中。

    • 釋放MYSQL_RES資源。

  • 返回好友列表vector<User>


類成員變量

// 目前該類沒有顯式成員變量,只有成員方法

備注:

  • 這個類設計中沒有持久的成員變量,每次操作時都新建MySQL對象以連接數據庫。這是一種簡單實現,適合關系不頻繁變動的場景。

其他細節

  • 數據庫操作:

    • 插入關系時調用mysql.update(sql),沒有事務控制,簡單插入。
    • 查詢好友信息時調用mysql.query(sql),處理結果集。
  • 安全性:

    • 使用sprintf拼接SQL語句,可能存在SQL注入風險;實際應用中建議用參數化查詢。
  • 性能:

    • 每次操作都新建數據庫連接,不復用連接,可能影響效率。

總結

這個FriendModel類提供了兩個主要功能

  1. 添加好友關系insert()

    • 將兩用戶的好友關系存入數據庫。
  2. 查詢好友列表query()

    • 根據用戶ID,查找所有好友信息,返回User對象的數組(好友列表)。

該設計結構簡潔,直接,但在性能優化和安全性方面可以進一步增強(如連接池、參數化查詢等)。

同時,它依賴于數據庫表結構(friend表和user表)合理設計。

3.類比FriendModel類,還有usermodel, groupmodel,OfflinMegModel

這是數據操作模型層,負責在業務邏輯和數據存儲之間傳遞信息。它知道如何把業務需求(如添加好友)轉化為數據庫操作,也懂得如何把數據庫查詢結果轉化為業務可用的對象。

4.group.hpp,groupuser.hpp,user.hpp

?讓我們詳細分析這三個類(UserGroupUserGroup)的設計與作用,它們都是典型的 ORM(對象關系映射)類,用于映射數據庫中的表結構到 C++ 對象中,方便開發中對數據的操作。

1.?User?類(用戶基礎表的ORM類)

class User
{
public:User(int id = -1, string name = "", string pwd = "", string state = "offline"){this->id = id;this->name = name;this->password = pwd;this->state = state;}void setId(int id) { this->id = id; }void setName(string name) { this->name = name; }void setPwd(string pwd) { this->password = pwd; }void setState(string state) { this->state = state; }int getId() { return this->id; }string getName() { return this->name; }string getPwd() { return this->password; }string getState() { return this->state; }protected:int id;string name;string password;string state;
};

作用與設計:

  • 表示“用戶”實體,將用戶表中的字段(id,?name,?password,?state)映射為類成員。
  • 提供了構造函數、getter、setter方法,方便操作。
  • 典型的ORM類,用于在程序中封裝用戶信息。

2.?GroupUser?類(群組用戶,擴展了User,添加角色信息)

復制代碼

class GroupUser : public User
{
public:void setRole(string role) { this->role = role; }string getRole() { return this->role; }private:string role; // 用戶在群組中的角色(如管理員、普通成員)
};

作用與設計:

  • 繼承自User,意味著“群組內的用戶”具有用戶的所有基本屬性(如ID、用戶名、密碼、狀態)。
  • 增加role屬性,表示用戶在群組中的角色。
  • 設計理念:一個群組中的用戶不僅僅算作普通用戶,還需區分角色,因此在群組關系中,添加了角色字段。

總結:

  • GroupUser是一個“關系類”,表示某個用戶在某個群組中的身份信息(不僅僅是用戶,還包括角色屬性)。
  • 這樣設計方便在多對多關系中存儲角色信息(如用戶屬于哪個群,角色怎樣)。

3.?Group?類(群組類)

class Group
{
public:Group(int id = -1, string name = "", string desc = ""){this->id = id;this->name = name;this->desc = desc;}void setId(int id) { this->id = id; }void setName(string name) { this->name = name; }void setDesc(string desc) { this->desc = desc; }int getId() { return this->id; }string getName() { return this->name; }string getDesc() { return this->desc; }vector<GroupUser> &getUsers() { return this->users; }private:int id;               // 群組IDstring name;          // 群組名稱string desc;          // 群組描述vector<GroupUser> users; // 包含的成員列表(每個成員帶角色信息)
};

作用與設計:

  • 表示“群組”實體,映射群組表。
  • 含有ID、名稱、描述字段。
  • users?成員變量用來存儲群組的所有成員(GroupUser對象),實現多對多關系(一個群組有多個用戶,一用戶可能在多個群組)。
  • 提供了基本的getter/setter方法。

關系與整體架構理解

  • User:代表整個系統的用戶信息,映射用戶表。
  • GroupUser:繼承用戶信息,并附加角色信息,代表某用戶在特定群組中的角色(多對多關系中的中間表/關系實體)。
  • Group:代表群組實體,含其成員(GroupUser的集合)信息。

這套設計體現了面向對象方式對數據庫關系的封裝:

  • 通過Group類管理群組信息和成員列表;
  • 通過GroupUser類描述用戶和角色之間的關系;
  • 通過User類描述用戶基礎信息。

總結:

  • 這三類共同組成了一個與“群組聊天”功能相關的對象模型架構。
  • 設計充分利用繼承(GroupUser繼承User)來復用代碼,增強擴展性。
  • 使得程序能夠以對象的方式操作數據庫中的用戶、群組與成員關系。

5.Redis?類

這份redis.hppredis.cpp實現了一個封裝了Redis發布訂閱(Pub/Sub)功能的類,方便在C++項目中使用Redis進行消息通信。下面我會詳細介紹這個類中的成員變量和成員函數,幫助你理解它的設計思想和使用方法。


Redis?類成員和方法詳解

1.?成員變量

復制代碼

private:redisContext *_publish_context;   // 負責發布消息的上下文redisContext *_subcribe_context;  // 負責訂閱消息的上下文function<void(int, string)> _notify_message_handler; // 消息接收時的回調函數
  • _publish_context:用來連接和操作Redis的發布(PUBLISH)命令,用于向頻道發送消息。
  • _subcribe_context:用來連接和操作Redis的訂閱(SUBSCRIBE)命令,用于監聽和接收頻道的消息。
  • _notify_message_handler:業務層定義的回調函數,用于通知業務層收到的消息。

2.?構造函數和析構函數

復制代碼

Redis();  // 構造函數,初始化成員指針為空
~Redis(); // 析構函數,釋放連接
  • 構造函數:初始化指針為nullptr
  • 析構函數:如果指針不為空,調用redisFree()釋放資源,避免內存泄漏。

3.?核心功能成員方法

connect()

復制代碼

bool connect();
  • 連接本地Redis服務器(默認地址為?127.0.0.1:6379)。
  • 分別創建兩個連接:一個用于發布,一個用于訂閱。
  • 連接成功后,啟動一個獨立線程調用observer_channel_message(),不斷監聽訂閱通道中的消息,異步通知業務層。
publish()

復制代碼

bool publish(int channel, string message);
  • 向指定頻道發布消息。
  • 使用redisCommand()執行PUBLISH命令。
  • 如果成功,返回true,否則返回false
subscribe()

復制代碼

bool subscribe(int channel);
  • 訂閱指定頻道。
  • 使用redisAppendCommand()redisBufferWrite()向Redis服務器發出SUBSCRIBE命令。
  • 注意:訂閱操作是非阻塞的,只是發出訂閱命令,接收消息由后臺線程監聽。
unsubscribe()

復制代碼

bool unsubscribe(int channel);
  • 取消訂閱某個頻道。
  • 使用類似subscribe()的方式發出UNSUBSCRIBE命令。
observer_channel_message()

復制代碼

void observer_channel_message();
  • 運行在后臺線程中,持續調用redisGetReply()接收Redis服務器推送的消息。
  • 解析收到的消息數組(reply->element):
    • reply->element[2]->str:消息內容
    • reply->element[1]->str:頻道編號
  • 調用_notify_message_handler()通知業務層收到新消息。
init_notify_handler()

復制代碼

void init_notify_handler(function<void(int, string)> fn);
  • 初始化消息通知的回調函數。
  • 用戶在業務層定義處理邏輯后,調用此函數注冊。

其他細節說明

  • 連接管理:在connect()中創建兩個redisContext,方便分別處理發布和訂閱的連接,避免競爭和阻塞。
  • 多線程
    • 訂閱消息的監聽在observer_channel_message()中運行在單獨的線程,保證異步處理。
  • 內存管理
    • 使用freeReplyObject()釋放redisReply資源。
  • 命令執行
    • 發布:redisCommand()
    • 訂閱/取消訂閱:redisAppendCommand()?+?redisBufferWrite(),避免阻塞調用。
  • 錯誤處理
    • 連接失敗和命令失敗都輸出錯誤信息,返回false

總結:

這個Redis類封裝了Redis的Pub/Sub基本操作:

  • 連接connect()
  • 發布消息publish()
  • 訂閱頻道subscribe()
  • 取消訂閱unsubscribe()
  • 異步監聽消息:在后臺線程中自動調用observer_channel_message(),收到消息后通過回調通知業務邏輯。
  • 回調機制:用戶可以注冊自己的消息處理函數。

它實現了異步訂閱模型,能很好地應用在類似聊天服務器、實時通知等場景,非常實用。

6.ChatServer類

你的代碼基于知名的?Muduo?網絡庫,實現了一個高效、易維護的聊天服務器主控類。下面按照結構–成員變量–成員方法詳細分析:


一、ChatServer 類的結構作用

ChatServer 封裝了實際的網絡服務器邏輯,負責:

  • 初始化服務器、設置參數
  • 監聽和管理客戶端連接
  • 管理消息收發和業務處理
  • 調度事件循環(事件驅動)

二、成員變量講解

private:TcpServer _server; // 組合muduo庫,實現服務器功能的類對象EventLoop *_loop;  // 指向事件循環對象(相當于主線程的事件分發器)

1.?TcpServer _server

  • Muduo 中封裝的 TCP 服務器核心類。
  • 能自動監聽端口,負責底層 epoll、多線程 I/O、連接管理等“苦力活”。
  • ChatServer 通過它實現與客戶端的所有網絡交互。

2.?EventLoop *_loop

  • 事件循環指針。
  • 實際上是主 Reactor 的事件分發器,用于響應 I/O 事件。

三、成員方法詳解

構造函數:ChatServer(EventLoop*, const InetAddress&, const string&)

ChatServer(EventLoop *loop, const InetAddress &listenAddr, const string &nameArg): _server(loop, listenAddr, nameArg), _loop(loop)
{// 注冊鏈接回調_server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));// 注冊消息回調_server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));// 設置線程數量_server.setThreadNum(4);
}

作用總結與細節分析:

  • 初始化?_server,告訴 muduo 用哪臺主機 IP/端口監聽(listenAddr)、取什么名字(nameArg)、用哪個 event loop。
  • setConnectionCallback():綁定"有新連接/斷開"時執行?onConnection?方法。
  • setMessageCallback():綁定"收到消息"時執行?onMessage?方法。
  • setThreadNum(4):用 4 個工作線程處理 I/O,提升吞吐量。

void start()

void ChatServer::start()
{_server.start();
}
  • 真正地啟動服務器(讓 Muduo 開始監聽并接收客戶端連接)。

void onConnection(const TcpConnectionPtr&)

void ChatServer::onConnection(const TcpConnectionPtr &conn)
{// 客戶端斷開鏈接if (!conn->connected()){ChatService::instance()->clientCloseException(conn);conn->shutdown();}
}

核心作用:

  • 這是連接狀態變化時的回調(新連接或斷開)。
  • 如果連接斷開,則做以下處理:
    • 通知業務服務層進行賬號、數據等“下線”處理(clientCloseException)。
    • 主動關閉 socket(conn->shutdown())。

void onMessage(const TcpConnectionPtr&, Buffer *, Timestamp)

void ChatServer::onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time)
{string buf = buffer->retrieveAllAsString();try {json js = json::parse(buf);// 檢查必須字段if (!js.contains("msgid")) {throw json::exception("Missing 'msgid' field");}auto msgHandler = ChatService::instance()->getHandler(js["msgid"].get<int>());msgHandler(conn, js, time);}catch (const json::exception& e) {LOG_ERROR << "JSON parse error: " << e.what();if (conn->connected()) {json response;response["msgid"] = INVALID_MSG_ACK;response["errno"] = 400;response["errmsg"] = "Invalid JSON format: " + string(e.what());conn->send(response.dump());}}catch (const exception& e) {LOG_ERROR << "Message handling error: " << e.what();}
}

詳細流程:

  1. 拿到完整緩沖區數據(收到的一次消息)。
  2. 嘗試解析為 JSON 對象。
  3. 檢查是否有"msgid"字段(決定了本消息應該怎么處理)。
  4. 調用 ChatService::instance()->getHandler(msgid) 找到對應業務處理函數,然后把消息傳給它執行。
  5. 如果解析失敗/消息結構有誤,則:
    • 打日志報錯
    • 給客戶端返回錯誤提示

作用通俗理解:

“服務器收到客戶發來的消息,把這消息當成快遞包裹拆開看——快遞單上寫著包裹類型(msgid),然后根據類型分給對應員工(handler)處理。拆快遞出錯就寫個反饋單還回去。”


四、補充說明

成員方法之間的關系

  • start?用于啟動服務器。
  • onConnection/onMessage?并不是主動調用,而是事件驅動,在有連接/消息時被 Muduo 框架自動調用。
  • 事件響應機制和reactor思想一致。

和 ChatService 的協作

ChatServer 只管網絡部分,具體業務如何處理交由 ChatService,“專人專職”,大大降低耦合。


五、總結

ChatServer 核心成員&方法概覽:

名稱簡要作用
_serverMuduo 的 TCP 服務器,負責監聽和通信。
_loop名字叫event loop,事件派發中心。
start()啟動服務器。
onConnection()檢測連接/斷開,并在斷開時交給業務進一步下線處理。
onMessage()收到客戶端數據時解析json,根據業務類型分發給具體handler處理;出問題自動回錯。
構造函數初始化服務器參數并注冊各回調,每一步都讓ChatServer做最擅長的事情。

這樣,不僅代碼結構清晰,而且便于擴展和維護,是高性能網絡服務推薦的模式!

7.chatservice類

會把這個ChatService類中的所有成員變量和方法,逐個詳細講解,確保你理解每個部分的作用、設計思想以及它們的工作流程,而且會盡力用通俗易懂的語言,讓你像聽故事一樣理解這個“聊天服務”系統。


一、類的整體架構和角色定位

ChatService是一個“聊天服務器”的核心業務類(管理者)。它采用單例模式(確保整個程序中只存在唯一的實例),負責處理所有的“用戶登錄、注冊、點對點聊天、群聊、好友關系、群組操作”等業務邏輯。


二、成員變量(這個類內部存的“存儲空間”和“工具箱”)

1.?單例實例

static ChatService *instance();
  • 作用:提供調用的入口,用于獲取或創建唯一的ChatService實例(即單例設計模式)。
  • 通俗理解:就像超市只有一個“管理員”實例,這個方法幫你拿到它。

2.?消息處理映射表

unordered_map<int, MsgHandler> _msgHandlerMap;
  • 作用:把不同“消息ID”映射到對應“處理函數”。
  • 比如登錄對應login()函數,注冊對應reg()函數。
  • 通俗理解:就像菜單點菜表,點“牛排”就知道要做牛排一樣。

3.?在線用戶連接表

unordered_map<int, TcpConnectionPtr> _userConnMap;
  • 作用:存放已登錄、在線用戶的“用戶ID”和“對應的TCP連接指針”。
  • 用途:當需要向某用戶發消息時,先找這個表看用戶是否在線,在線了就可以直接推送消息。
  • 通俗理解:像“電話簿”,存著每個誰在線、用哪個電話(連接)。

4.?連接互斥鎖

mutex _connMutex;
  • 作用:在多線程環境下,保證對_userConnMap的操作(增刪改查)是安全的。
  • 為何需要:因為可能有多個連接同時操作這個表,比如一個用戶剛登錄、登出,或服務器在同時處理多個請求。

5.?數據模型對象(操作數據庫)

復制代碼

UserModel _userModel;
OfflineMsgModel _offlineMsgModel;
FriendModel _friendModel;
GroupModel _groupModel;
  • 作用:封裝了數據庫操作的封裝類(操作用戶信息,離線消息,好友關系,群組信息)。
  • 例子
    • _userModel:查詢或更新用戶信息。
    • _offlineMsgModel:存儲或讀取離線消息。
    • _friendModel:管理好友關系(添加好友等)。
    • _groupModel:管理群聊信息。

6.?Redis操作對象

復制代碼

Redis _redis;
  • 作用:封裝了與Redis(一個高速的數據庫和消息隊列系統)的交互,用于實現“跨進程”、“多服務器”的消息推送和訂閱。

三、主要方法(業務邏輯執行的“技能”)

1.?獲取單例實例

復制代碼

static ChatService *instance();
  • 作用:返回全局唯一的ChatService對象,確保程序中只有一個“聊天服務”。
  • 實現:用“靜態局部變量”,保證只創建一次。

2.?構造函數(注冊消息-handler)

復制代碼

ChatService::ChatService()
  • 作用
    • 初始化各種消息ID和對應的請求處理函數的映射關系。
    • 鏈接(連接)Redis,為后續消息訂閱做準備。
注冊消息響應(通過bind綁定對應成員函數)
  • login注冊到LOGIN_MSG

  • register注冊到REG_MSG

  • oneChat注冊到ONE_CHAT_MSG

  • 還有好友添加、創建群、加入群、群聊的處理方法。

  • 通俗理解:像打電話時撥通不同號碼調用不同小姐姐的服務。


3.?業務重置(重要)

復制代碼

void reset();
  • 作用
    • 在服務器異常或重啟時調用。
    • 將所有“已登錄的用戶”狀態重置為“離線”狀態(確保狀態同步真實情況)。
  • 調用場景:系統崩潰后,恢復正常工作狀態。

4.?獲取特定“消息類型”對應的處理函數

復制代碼

MsgHandler getHandler(int msgid);
  • 作用
    • 輸入msgid,返回對應的處理函數(函數指針或lambda)。
    • 如果找不到,就返回一個“錯誤處理器”。
  • 設計亮點
    • 動態路由:前端發的不同消息會被路由到不同的處理函數去。

5.?各類具體業務的處理方法(詳細講解)

login()登陸流程
  • 輸入:用戶ID、密碼。
  • 邏輯:-查詢用戶信息,驗證密碼。-用戶已在線,提示重復登錄,否則:-存用戶名和連接關系到_userConnMap,方便推送。-在Redis訂閱頻道(方便跨服務通知)。-更新用戶狀態為在線。-返回用戶信息(包括離線消息、好友列表、群組信息)。
  • 作用:實現實際“登陸”功能。
reg()注冊
  • 輸入:用戶名、密碼。
  • 邏輯:-校驗格式、長度。-存入數據庫。-返回成功或失敗。
loginout()退出/注銷
  • 作用:-從活躍連接中刪除用戶。-在Redis取消訂閱某渠道(讓其他服務器知道該用戶已離線)。-更新數據庫用戶狀態。
clientCloseException()
  • 作用:
    • 處理客戶端異常斷開連接(比如突然關閉窗口,沒有正常退出)。
    • 類似loginout,清除連接,更新狀態。
oneChat()點對點聊天
  • 輸入:接收方toid,消息內容。
  • 機制:-第一步:找_userConnMap,看看對方是否在線(直接推)。-第二步:如不在線,則存離線消息。-第三步:如果對方在線但不在自己服務器(假設多節點),就用Redis發布消息,其他節點會收信息。
addFriend()添加好友
  • 輸入:自己ID和好友ID。
  • 操作:存入好友關系表。
createGroup()
  • 輸入:發起人ID、群組名、描述。
  • 操作:-在數據庫中創建新群。-將發起人加入該群(權限設為“creator”)。
addGroup()加入群組
  • 輸入:用戶ID、群ID。
  • 操作:將用戶加入群組。
groupChat()群聊
  • 輸入:群ID、群成員消息。
  • 作用:
    • 中轉消息:
      • 先找成員在不在線(在自己連接map里)。
      • 在線了推送。
      • 不在線存離線消息(存到數據庫中)。

6.?處理Redis訂閱消息

復制代碼

void handleRedisSubscribeMessage(int, string);
  • 作用
    • 其他服務器發布同步消息后,調用此函數。
    • 找到對應的用戶連接,如果在,推送消息。
    -否則,存離線消息。

四、重點總結(通俗版)

  • ChatService就像一個“指揮官”,協調所有聊天相關的操作。
  • 存放“用戶和連接關系”,方便主動“發消息給用戶”。
  • 有事注冊“事件處理器”:不同的ID對應不同操作(登錄、注冊、聊天、群聊)。
  • 利用Redis實現多服務消息同步
  • 每個業務都是封裝好的函數:登錄、注冊、點對點聊天等。
  • 整體設計高擴展性、線程安全、結構清晰。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/91560.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/91560.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/91560.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電纜安全雙保險:不止防盜,更能防觸電的塔能智慧照明守護方案

城市照明、地下車庫以及園區路燈所涉及的電纜安全問題&#xff0c;向來都是運維管理方面頗為棘手的難題。在傳統的運維管理模式之下&#xff0c;電纜一旦被盜&#xff0c;那么所造成的影響可不小&#xff0c;一方面會帶來直接的經濟損失&#xff0c;另一方面還極有可能因為線路…

Leetcode刷題營第二十九,三十題:二叉樹的中序以及后序遍歷

94.二叉樹的中序遍歷 給定一個二叉樹的根節點 root &#xff0c;返回 它的 中序 遍歷 。 示例 1&#xff1a; 輸入&#xff1a;root [1,null,2,3] 輸出&#xff1a;[1,3,2]示例 2&#xff1a; 輸入&#xff1a;root [] 輸出&#xff1a;[]示例 3&#xff1a; 輸入&#x…

Rabbitmq Direct Exchange(直連交換機)可以保證消費不被重復消費嗎,可以多個消費者,但是需要保證同一個消息,不會被投遞給多個消費者

在 RabbitMQ 中&#xff0c;默認情況下&#xff0c;不能保證消息不被重復消費&#xff0c;但可以通過 隊列綁定方式 消費者競爭機制 來確保 同一消息只被一個消費者處理。以下是幾種可行的方案&#xff1a;方案 1&#xff1a;單隊列 競爭消費者模式&#xff08;默認行為&…

常用的OTP語音芯片有哪些?

唯創知音在 OTP 語音芯片有著26年的歷史&#xff0c;有著豐富的技術積累與產品迭代歷程。1999 年&#xff0c;唯創知音在廣州成立&#xff0c;彼時便開始在電子領域積極探索。2000 年&#xff0c;公司敏銳捕捉到語音芯片行業的發展潛力&#xff0c;正式進軍該領域。經過數年技術…

分布式光伏發電系統中的“四可”指的是什么?

在分布式光伏電站規模爆發式增長的今天&#xff0c;“看不見、管不住、調不動”的難題卻成為行業痛點。如何讓散布各處的光伏電站真正成為穩定高效的“綠色能量站”&#xff1f;2025年《分布式光伏發電開發建設管理辦法》大型工商業項目&#xff08;≥6MW&#xff09;明確要求具…

健康管理系統新趨勢:AI + 物聯網如何重塑健康管理

一、傳統健康管理的痛點與變革之必然長久以來&#xff0c;我們熟悉的健康管理方式存在明顯局限&#xff1a;數據孤島嚴重&#xff1a;體檢報告在抽屜里沉睡&#xff0c;健身手環數據僅存于手機&#xff0c;不同醫療機構信息互不相通&#xff0c;個人健康信息猶如碎片散落各處。…

git基本操作【GIT-2】

git基本操作初始化一個倉庫&#xff08;repository&#xff09;、開始或停止跟蹤&#xff08;track&#xff09;文件、暫存&#xff08;stage&#xff09;或提交&#xff08;commit&#xff09;更改如何配置 Git 來忽略指定的文件和文件模式、如何迅速而簡單地撤銷錯誤操作、如…

【數據準備】——深度學習.全連接神經網絡

目錄 1 數據加載器 1.1 構建數據類 1.1.1 Dataset類 1.1.2 TensorDataset類 1.2 數據加載器 2 數據加載案例 2.1 加載csv數據集 2.2 加載圖片數據集 2.3 加載官方數據集 2.4 pytorch實現線性回歸 1 數據加載器 分數據集和加載器2個步驟~ 1.1 構建數據類 1.1.1 Dat…

健康生活,從細節開始

健康生活&#xff0c;從細節開始在當今快節奏的生活中&#xff0c;健康逐漸成為人們關注的焦點。擁有健康的身體&#xff0c;才能更好地享受生活、追求夢想。那么&#xff0c;如何才能擁有健康呢&#xff1f;這就需要我們從生活中的點滴細節入手&#xff0c;培養良好的生活習慣…

javax.servlet.http.HttpServletResponse;API導入報錯解決方案

javax.servlet.http.HttpServletResponse;API導入報錯解決方案與Postman上傳下載文件驗證 1. 主要錯誤&#xff1a;缺少 Servlet API 依賴 錯誤信息顯示 javax.servlet.http 包不存在。這是因為你的項目缺少 Servlet API 依賴。 解決方案&#xff1a; 如果你使用的是 Maven&…

reids依賴刪除,但springboot仍然需要redis參數才能啟動

背景&#xff1a;項目需要刪除redis。我刪除完項目所有配置redis的依賴&#xff0c;啟動報錯。[2025-07-17 15:08:37:561] [DEBUG] [restartedMain] DEBUG _.s.w.s.H.Mappings - [detectHandlerMethods,295] [] - o.s.b.a.w.s.e.BasicErrorController:{ [/error]}: error(HttpS…

【前端】CSS類命名規范指南

在 CSS 中&#xff0c;合理且規范的 class 命名格式對項目的可維護性和協作效率至關重要。以下是主流的 class 命名規范和方法論&#xff1a;一、核心命名原則語義化命名&#xff1a;描述功能而非樣式 ? .search-form&#xff08;描述功能&#xff09;? .red-text&#xff08…

C++網絡編程 4.UDP套接字(socket)編程示例程序

以下是基于UDP協議的完整客戶端和服務器代碼。UDP與TCP的核心區別在于無連接特性&#xff0c;因此代碼結構會更簡單&#xff08;無需監聽和接受連接&#xff09;。 UDP服務器代碼&#xff08;udp_server.cpp&#xff09; #include <iostream> #include <sys/socket.h&…

King’s LIMS:實驗室數字化轉型的智能高效之選

實驗室數字化轉型不僅是技術升級&#xff0c;更是管理理念和工作方式的革新。LIMS系統作為這一轉型的核心工具&#xff0c;能夠將分散的實驗數據轉化為可分析、可復用的資產&#xff0c;為科研決策提供支持&#xff1b;規范檢測流程&#xff0c;減少人為干預&#xff0c;確保結…

【力扣 中等 C】97. 交錯字符串

目錄 題目 解法一 題目 待添加 解法一 bool isInterleave(char* s1, char* s2, char* s3) {const int len1 strlen(s1);const int len2 strlen(s2);const int len3 strlen(s3);if (len1 len2 ! len3) {return false;}if (len1 < len2) {return isInterleave(s2, s1,…

Class9簡潔實現

Class9簡潔實現 %matplotlib inline import torch from torch import nn from d2l import torch as d2l# 初始化訓練樣本、測試樣本、樣本特征維度和批量大小 n_train,n_test,num_inputs,batch_size 20,100,200,5 # 設置真實權重和偏置 true_w,true_b torch.ones((num_inputs…

ELK日志分析,涉及logstash、elasticsearch、kibana等多方面應用,必看!

目錄 ELK日志分析 1、下載lrzsc 2、下載源包 3、解壓文件,下載elasticsearch、kibana、 logstash 4、配置elasticsearch 5、配種域名解析 6、配置kibana 7、配置logstash 8、進行測試 ELK日志分析 1、下載lrzsc [rootlocalhost ~]# hostnamectl set-hostname elk ##…

終極剖析HashMap:數據結構、哈希沖突與解決方案全解

文章目錄 引言 一、HashMap底層數據結構&#xff1a;三維存儲架構 1. 核心存儲層&#xff08;硬件優化設計&#xff09; 2. 內存布局對比 二、哈希沖突的本質與數學原理 1. 沖突產生的必然性 2. 沖突概率公式 三、哈希沖突解決方案全景圖 1. 鏈地址法&#xff08;Hash…

1.1.5 模塊與包——AI教你學Django

1.1.5 模塊與包&#xff08;Django 基礎學習細節&#xff09; 模塊和包是 Python 項目組織和代碼復用的基礎。Django 項目本質上就是由多個模塊和包組成。理解和靈活運用模塊與包機制&#xff0c;是寫好大型項目的關鍵。 一、import、from-import、as 的用法 1. import 用于導入…

UE5 相機后處理材質與動態參數修改

一、完整實現流程1. 創建后處理材質材質設置&#xff1a;在材質編輯器中&#xff0c;將材質域(Material Domain)設為后處理(Post Process)設置混合位置(Blendable Location)&#xff08;如After Tonemapping&#xff09;創建標量/向量參數&#xff08;如Intensity, ColorTint&a…