C++搭建集群聊天室:用户单聊及离线消息处理功能实现

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++搭建集群聊天室:用户单聊及离线消息处理功能实现相关的知识,希望对你有一定的参考价值。


单聊

单聊功能其实很简单的,就是数据包的发送,接收。

public.h 现状:

#ifndef PUBLIC_H_
#define PUBLIC_H_

enum EnMsgType{
    LOGIN_TYPE = 1, //正常登录
    LOGIN_MSG_ACK,  //登录相应消息
    REG_TYPE,       //正常注册
    REG_MSG_ACK,    //注册相应消息
    ONE_CHAT_MSG,   //一对一聊天消息
    GROUP_CHAT_MSG, //群聊聊天消息
};

#endif

单聊,在chatservice.cpp 里面加一个函数,头文件自行修改。


//一对一聊天
void ChatService::onechat(const TcpConnectionPtr &conn,json &js,Timestamp time){
    int toid = js["to"].get<int>();

    // bool userstate = false;
    //开辟锁的作用域
    {
        lock_guard<mutex> lock(_connMutex);

        auto it = _userConnMap.find(toid);

        if(it != _userConnMap.end()){  
            //用户在线,转发消息
            it->second->send(js.dump());
            return;
        }   
    }
    
    //  有可能在发送的时候,连接就给断了
    // if(userstate){  //用户在线,转发消息

    // }
    // else{   //用户不在线,存储离线消息

    // }
    
    //存储离线消息
    _offlineMsgmodel.insert(toid,js.dump());
}

直接聊就这样。

比较复杂一点的是处理离线消息。


离线消息处理

开一个专门处理离线消息的文件,offlinemessagemodel.cpp,头文件配上。

#ifndef OFFLINEMESSAGEMODEL_H_
#define OFFLINEMESSAGEMODEL_H_

#include<string>
#include<vector>

using namespace std;


class OfflineMessageModel{
public:
    //存储用户离线消息
    void insert(int user_id,string msg);

    //删除用户离线消息
    void remove(int userid);

    //读取用户离线消息
    vector<string> query(int userid);
    
private:
    
};

#endif

offlinemessagemodel.cpp 实现:

#include "db.hpp"
#include "offlinemessagemodel.hpp"

//存储用户离线消息
void OfflineMessageModel::insert(int userid, string msg){
    char sql[128] = {0};

    sprintf(sql, "insert into offlinemessage values(%d, '%s')", userid, msg.c_str());
    mysql mysql;
    if(mysql.connect()){
        mysql.update(sql);
    }
}

//删除用户离线消息
void OfflineMessageModel::remove(int userid){
    char sql[128] = {0};

    sprintf(sql,"delete from OfflineMessage where userid = %d",userid);

    MySQL mysql;
    if(mysql.connect()){
        mysql.update(sql);
    }
}

//读取用户离线消息
vector<string> OfflineMessageModel::query(int userid){
    // 1.组装sql语句
    char sql[1024] = {0};
    sprintf(sql, "select message from OfflineMessage where userid = %d", userid);

    vector<string> vec;

    MySQL mysql;
    if (mysql.connect())
    {
        MYSQL_RES *res = mysql.query(sql);
        
        if (res != nullptr)
        {

            MYSQL_ROW row;
            while((row = mysql_fetch_row(res)) != nullptr){
                vec.push_back(row[0]);
            }
            mysql_free_result(res);
        }
    }
    return vec;
}

离线消息调用逻辑

1、在发送消息的时候判断对方是否在线,不在线则存储离线消息。
2、在登录的时候加载离线消息,一般消息会很多,所以用容器来存储。
3、容器选择vector,便于直接序列化/反序列化。

第一、三点在上面展示了,第二点:

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.getpassword() == pwd)
    {
        if (user.getstate() == "online")
        {
            // 该用户已经登录,不允许重复登录
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 2;
            response["errmsg"] = "this account is using, input another!";
            
            //查询用户是否有离线消息
            vector<string> vec = _offlineMsgmodel.query(id);
            if(!vec.empty()){
                response["offlinemsg"] = vec;

                //清空离线消息
                _offlineMsgmodel.remove(id);
            }

            conn->send(response.dump());
        }
        else
        {   
            //添加作用域,限制锁的粒度
            {
                lock_guard<mutex> lock(_connMutex);
          
                //记录用户连接
                _userConnMap.insert({id,conn});
            }
            
            // 登录成功,更新用户状态信息 state offline=>online
            user.setstate("online");
            _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"] = "id or password is invalid!";
        conn->send(response.dump());
    }
}

前面代码修改

发现前面的SQL语句中将表 User 写成了 user,这里需要改一下

以上是关于C++搭建集群聊天室:用户单聊及离线消息处理功能实现的主要内容,如果未能解决你的问题,请参考以下文章

C++搭建集群聊天室(十四):群聊功能

C++搭建集群聊天室(十四):群聊功能

Centos8安装在线及离线K8S集群搭建

功能测试--聊天功能测试&微信聊天

IM系统:消息推送及离线存储

C++搭建集群聊天室(十八):nginx + redis 发布订阅 升级项目为集群服务器