TeamTalk消息服务器(群组相关)

news2024/9/18 13:18:22

具体的流程如下介绍,后续需要着重研究数据库相关表的结构设计。

群组信令和协议设计

enum GroupCmdID {
  CID_GROUP_NORMAL_LIST_REQUEST = 1025,
  CID_GROUP_NORMAL_LIST_RESPONSE = 1026,
  CID_GROUP_INFO_REQUEST = 1027,
  CID_GROUP_INFO_RESPONSE = 1028,
  // ...... 暂时省略无关信令
};

message IMNormalGroupListReq{
	//cmd id:			0x0401
	required uint32 user_id = 1;
	optional bytes attach_data = 20;
}

message IMNormalGroupListRsp{
	//cmd id:			0x0402
	required uint32 user_id = 1;
	repeated IM.BaseDefine.GroupVersionInfo group_version_list = 2;
	optional bytes attach_data = 20;
}

message GroupVersionInfo{
	required uint32 group_id = 1;
	required uint32 version = 2; // 不同级别的群??	
}

message IMGroupInfoListReq{
	//cmd id:			0x0403
	required uint32 user_id = 1; // 用户ID
	repeated IM.BaseDefine.GroupVersionInfo group_version_list = 2; // 群ID列表
	optional bytes attach_data = 20;
}

message IMGroupInfoListRsp{
	//cmd id:			0x0404
	required uint32 user_id = 1;
	repeated IM.BaseDefine.GroupInfo group_info_list = 2;
	optional bytes attach_data = 20;
}

message GroupInfo{
	required uint32 group_id = 1;
	required uint32 version = 2;
	required string group_name = 3;
	required string group_avatar = 4;
	required uint32 group_creator_id = 5;
	required GroupType group_type = 6;
	required uint32 shield_status = 7;		//1: shield  0: not shield 
	repeated uint32 group_member_list = 8;
}

请求用户群组ID

流程图

请添加图片描述

具体代码逻辑

db_proxy_server 收到 CID_GROUP_NORMAL_LIST_REQUEST后调用 DB_PROXY::getNormalGroupList 函数
主要就是读取用户ID所在群的ID列表

// group
m_handler_map.insert(make_pair(uint32_t(CID_GROUP_NORMAL_LIST_REQUEST), DB_PROXY::getNormalGroupList));

	/**
     *  获取正式群列表
     *
     *  @param pPdu      收到的packet包指针
     *  @param conn_uuid 该包过来的socket 描述符
     */
void getNormalGroupList(CImPdu* pPdu, uint32_t conn_uuid)
{
        IM::Group::IMNormalGroupListReq msg;
        IM::Group::IMNormalGroupListRsp msgResp;
        if(msg.ParseFromArray(pPdu->GetBodyData(), pPdu->GetBodyLength()))
        {
            CImPdu* pPduRes = new CImPdu;
            
            uint32_t nUserId = msg.user_id();
            
            list<IM::BaseDefine::GroupVersionInfo> lsGroup;
            // 关键函数
            CGroupModel::getInstance()->getUserGroup(nUserId, lsGroup, IM::BaseDefine::GROUP_TYPE_NORMAL);
            msgResp.set_user_id(nUserId);
            for(auto it=lsGroup.begin(); it!=lsGroup.end(); ++it)
            {
                IM::BaseDefine::GroupVersionInfo* pGroupVersion = msgResp.add_group_version_list();
                pGroupVersion->set_group_id(it->group_id());
                pGroupVersion->set_version(it->version());
            }
            
            log("getNormalGroupList. userId=%u, count=%d", nUserId, msgResp.group_version_list_size());
            
            msgResp.set_attach_data(msg.attach_data());
            pPduRes->SetPBMsg(&msgResp);
            pPduRes->SetSeqNum(pPdu->GetSeqNum());
            pPduRes->SetServiceId(IM::BaseDefine::SID_GROUP);
            pPduRes->SetCommandId(IM::BaseDefine::CID_GROUP_NORMAL_LIST_RESPONSE);
            CProxyConn::AddResponsePdu(conn_uuid, pPduRes);
        }
        else
        {
            log("parse pb failed");
        }
}

void CGroupModel::getUserGroup(uint32_t nUserId, list<IM::BaseDefine::GroupVersionInfo>& lsGroup, uint32_t nGroupType)
{
    list<uint32_t> lsGroupId;
    getUserGroupIds(nUserId, lsGroupId,0);
    if(lsGroupId.size() != 0)
    {
        getGroupVersion(lsGroupId, lsGroup, nGroupType);
    }
}

void CGroupModel::getUserGroupIds(uint32_t nUserId, list<uint32_t>& lsGroupId, uint32_t nLimited)
{
    CDBManager* pDBManager = CDBManager::getInstance();
    CDBConn* pDBConn = pDBManager->GetDBConn("teamtalk_slave");
    if(pDBConn)
    {
        string strSql ;
        if (nLimited != 0) {
            strSql = "select groupId from IMGroupMember where userId=" + int2string(nUserId) + " and status = 0 order by updated desc, id desc limit " + int2string(nLimited);
        }
        else
        {
            strSql = "select groupId from IMGroupMember where userId=" + int2string(nUserId) + " and status = 0 order by updated desc, id desc";
        }
        
        CResultSet* pResultSet = pDBConn->ExecuteQuery(strSql.c_str());
        if(pResultSet)
        {
            while(pResultSet->Next())
            {
                uint32_t nGroupId = pResultSet->GetInt("groupId");
                lsGroupId.push_back(nGroupId);
            }
            delete pResultSet;
        }
        else
        {
            log("no result set for sql:%s", strSql.c_str());
        }
        pDBManager->RelDBConn(pDBConn);
    }
    else
    {
        log("no db connection for teamtalk_slave");
    }
}

请求用户群组具体信息

流程图

请添加图片描述

具体代码逻辑

DB_PROXY::getGroupInfo函数主要做:

  1. 解析user_id,解析群ID列表
  2. 根据群ID 通过 redis 缓存查找读取群成员列表
  3. 根据群ID通过数据库查找IMGroup 表获取对应群的详细信息
  4. 组包群信息以及群成员列表
// group
m_handler_map.insert(make_pair(uint32_t(CID_GROUP_INFO_REQUEST), DB_PROXY::getGroupInfo));

	/**
     *  获取群信息
     *
     *  @param pPdu      收到的packet包指针
     *  @param conn_uuid 该包过来的socket 描述符
     */
void getGroupInfo(CImPdu* pPdu, uint32_t conn_uuid)
{
        IM::Group::IMGroupInfoListReq msg;
        IM::Group::IMGroupInfoListRsp msgResp;
        if(msg.ParseFromArray(pPdu->GetBodyData(), pPdu->GetBodyLength()))
        {
            CImPdu* pPduRes = new CImPdu;
            uint32_t nUserId = msg.user_id();
            uint32_t nGroupCnt = msg.group_version_list_size();
            
            map<uint32_t, IM::BaseDefine::GroupVersionInfo> mapGroupId;
            for(uint32_t i=0; i<nGroupCnt; ++i)
            {
                IM::BaseDefine::GroupVersionInfo groupInfo = msg.group_version_list(i);
                // redis
                if(CGroupModel::getInstance()->isValidateGroupId(groupInfo.group_id()))
                {
                    mapGroupId[groupInfo.group_id()] = groupInfo;
                }
            }
            list<IM::BaseDefine::GroupInfo> lsGroupInfo;
            CGroupModel::getInstance()->getGroupInfo(mapGroupId, lsGroupInfo);
            
            msgResp.set_user_id(nUserId);
            for(auto it=lsGroupInfo.begin(); it!=lsGroupInfo.end(); ++it)
            {
                IM::BaseDefine::GroupInfo* pGroupInfo = msgResp.add_group_info_list();
                pGroupInfo->set_group_id(it->group_id());
                pGroupInfo->set_version(it->version());
                pGroupInfo->set_group_name(it->group_name());
                pGroupInfo->set_group_avatar(it->group_avatar());
                pGroupInfo->set_group_creator_id(it->group_creator_id());
                pGroupInfo->set_group_type(it->group_type());
                pGroupInfo->set_shield_status(it->shield_status());
                uint32_t nGroupMemberCnt = it->group_member_list_size();
                for (uint32_t i=0; i<nGroupMemberCnt; ++i) {
                    uint32_t userId = it->group_member_list(i);
                    pGroupInfo->add_group_member_list(userId);
                }
            }
            
            log("userId=%u, requestCount=%u", nUserId, nGroupCnt);
            
            msgResp.set_attach_data(msg.attach_data());
            pPduRes->SetPBMsg(&msgResp);
            pPduRes->SetSeqNum(pPdu->GetSeqNum());
            pPduRes->SetServiceId(IM::BaseDefine::SID_GROUP);
            pPduRes->SetCommandId(IM::BaseDefine::CID_GROUP_INFO_RESPONSE);
            CProxyConn::AddResponsePdu(conn_uuid, pPduRes);
        }
        else
        {
            log("parse pb failed");
        }
}
 // redis
bool CGroupModel::isValidateGroupId(uint32_t nGroupId)
{
    bool bRet = false;
    CacheManager* pCacheManager = CacheManager::getInstance();
    CacheConn* pCacheConn = pCacheManager->GetCacheConn("group_member");
    if(pCacheConn)
    {
        string strKey = "group_member_"+int2string(nGroupId);
        bRet = pCacheConn->isExists(strKey);
        pCacheManager->RelCacheConn(pCacheConn);
    }
    return bRet;
}

// mysql
void CGroupModel::getGroupInfo(map<uint32_t,IM::BaseDefine::GroupVersionInfo>& mapGroupId, list<IM::BaseDefine::GroupInfo>& lsGroupInfo)
{ 
   if (!mapGroupId.empty())
    {
       CDBManager* pDBManager = CDBManager::getInstance();
        CDBConn* pDBConn = pDBManager->GetDBConn("teamtalk_slave");
        if (pDBConn)
        {
            string strClause;
            bool bFirst = true;
            for(auto it=mapGroupId.begin(); it!=mapGroupId.end(); ++it)
            {
                if(bFirst)
                {
                    bFirst = false;
                    strClause = int2string(it->first);
                }
                else
                {
                    strClause += ("," + int2string(it->first));
                }
            }
            string strSql = "select * from IMGroup where id in (" + strClause  + ") order by updated desc";
            CResultSet* pResultSet = pDBConn->ExecuteQuery(strSql.c_str());
            if(pResultSet)
            {
                while (pResultSet->Next()) {
                    uint32_t nGroupId = pResultSet->GetInt("id");
                    uint32_t nVersion = pResultSet->GetInt("version");
               if(mapGroupId[nGroupId].version() < nVersion)
                    {
                        IM::BaseDefine::GroupInfo cGroupInfo;
                        cGroupInfo.set_group_id(nGroupId);
                        cGroupInfo.set_version(nVersion);
                        cGroupInfo.set_group_name(pResultSet->GetString("name"));
                        cGroupInfo.set_group_avatar(pResultSet->GetString("avatar"));
                        IM::BaseDefine::GroupType nGroupType = IM::BaseDefine::GroupType(pResultSet->GetInt("type"));
                        if(IM::BaseDefine::GroupType_IsValid(nGroupType))
                        {
                            cGroupInfo.set_group_type(nGroupType);
                            cGroupInfo.set_group_creator_id(pResultSet->GetInt("creator"));
                            lsGroupInfo.push_back(cGroupInfo);
                        }
                        else
                        {
                            log("invalid groupType. groupId=%u, groupType=%u", nGroupId, nGroupType);
                        }
                    }
                }
                delete pResultSet;
            }
            else
            {
                log("no result set for sql:%s", strSql.c_str());
            }
            pDBManager->RelDBConn(pDBConn);
       if(!lsGroupInfo.empty())
            {
                fillGroupMember(lsGroupInfo);
            }
        }
        else
        {
            log("no db connection for teamtalk_slave");
        }
    }
    else
    {
        log("no ids in map");
    }
}

最近联系会话信令和协议设计

enum BuddyListCmdID {
  CID_BUDDY_LIST_RECENT_CONTACT_SESSION_REQUEST = 513,
  CID_BUDDY_LIST_RECENT_CONTACT_SESSION_RESPONSE = 514,
  // ...... 暂时省略无关信令
  CID_BUDDY_LIST_USERS_STATUS_REQUEST = 522,
  CID_BUDDY_LIST_USERS_STATUS_RESPONSE = 523,
  // ...... 暂时省略无关信令
};


获取最近联系会话

流程图

具体代码逻辑

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

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

相关文章

pm2 + linux + nginx

pm2 pm2是一个用于管理node项目的工具 前言 有如下两个文件 index.js const express require("express"); const app express(); const port 9999;app.get("/index", (req, res) > {res.json({code:200,msg:"songzx001"}) });app.lis…

openGL笔记之序章

本系列来自 youtube 主播cherno的 openGL系列, b站双语版链接 https://www.bilibili.com/video/BV1Ni4y1o7Au?p1&vd_source0be7021510d651441db0edd576304997 openGL 是一个接口规范&#xff0c;定义了一系列接口&#xff0c;我们可以通过使用这些接口来一定程度的操作GP…

[Meachines] [Easy] Safe BOF+ROP链+.data节区注入BOF+函数跳转BOF+KeePass密码管理器密码破译

信息收集 IP AddressOpening Ports10.10.10.147TCP:22&#xff0c;80&#xff0c;1337 $ nmap -p- 10.10.10.147 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.4p1 Debian 10deb9u6 (protocol 2.0) | ssh-hostkey: | 2048 …

基于django的在线音乐网站设计/基于python的音乐播放系统

Django在线音乐网站设计 摘要&#xff1a;计算机网络如果结合使用信息管理系统&#xff0c;能够提高管理员管理的效率&#xff0c;改善服务质量。优秀的在线音乐网站设计能够更有效管理音乐资讯规范&#xff0c;帮助管理者更加有效管理音乐网站&#xff0c;可以帮助提高克服人工…

【深度剖析】《黑神话:悟空》员工薪资大曝光,你慕了吗?

《黑神话&#xff1a;悟空》引爆市场&#xff0c;员工薪资却成热议焦点&#xff01; 自8月20日解锁以来&#xff0c;《黑神话&#xff1a;悟空》以其国产3A游戏的高品质迅速占领市场&#xff0c;引发全球玩家的热烈讨论。深圳市游科互动科技有限公司开发的这款游戏&#xff0c…

C++20 是 C++ 语言的一次重大更新

C20 是 C 语言的一次重大更新&#xff0c;它引入了许多新特性&#xff0c;旨在提高开发者的生产力和代码的现代化。以下是 C20 的一些关键特性的总结&#xff1a; 模块&#xff08;Modules&#xff09;&#xff1a;C20 引入了模块&#xff0c;这是一种新的编译单元&#xff0c;…

CSS解析:定位和层叠上下文

许多开发人员对定位的理解很粗略&#xff0c;如果不完全了解定位&#xff0c;就很容易给自己挖坑。有时候可能会把错误的元素放在其他元素前面&#xff0c;要解决这个问题却没有那么简单。 一般的布局方法是用各种操作来控制文档流的行为。定位则不同&#xff1a;它将元素彻底…

mysql 不同版本安装不同端口

安装版本为Mysql8.0.11 先解压&#xff0c;解压后&#xff0c;包下创建my.ini文件内容如下&#xff1a; 注意&#xff1a;端口不能给别的mysql一样 [mysqld]# 设置3306端口port3307 # 自定义设置mysql的安装目录&#xff0c;即解压mysql压缩包的目录basedirD:\\rj\\mysql8.0.…

如何将 HEIC 转换为 JPG/PNG

“我刚刚将 iPhone 15 升级到 iOS 18&#xff0c;但新 iPhone 的照片现在变为 HEIC。什么是 HEIC 格式&#xff1f;我可以在 Windows 上使用 HEIC 照片吗&#xff1f;有人知道 HEIC 的技巧吗&#xff1f;谢谢&#xff01;” 当你换了一部新的iPhone时&#xff0c;你会发现你的图…

TCP协议 配合 Wireshark 分析数据

在TCP连接中&#xff0c;无论是客户端还是服务端&#xff0c;都有可能成为发送端或接收端&#xff0c;这是因为TCP是一个全双工协议&#xff0c;允许数据在同一连接中双向流动 客户端&#xff08;Client&#xff09;&#xff1a;通常是指主动发起连接请求的一方。例如&#xf…

商家推广怎么利用C#发送视频短信

视频短信&#xff0c;这一融合了视频、音频与文本的创新通信方式&#xff0c;不仅革新了传统短信的单一形式&#xff0c;更以其独特的魅力带领着移动通信的新风尚。它以移动视频格式为载体&#xff0c;实现了信息传输的多元化&#xff0c;为用户带来不一样的通信体验。 支持免费…

windows安全中心永久卸载工具分享

使用方法 博客&#xff1a;h0ck1r丶羽~从零到一 卸载工具下载链接&#xff1a; 夸克网盘分享 一路回车&#xff0c;选项Y即可 耐心等待几秒种&#xff0c;自动重启 此时打开windows安全中心&#xff0c;已经完全不能使用了&#xff0c;响应的杀毒功能也关了 往期推荐 【渗透测…

简易STL实现 | Deque的实现

一种 在内存中存储元素的数据结构&#xff0c;它支持 在两端添加和删除元素&#xff08;使用循环数组实现&#xff09; 1、deque的特性&#xff08;分段deque实现&#xff09; 1、双端操作&#xff1a; deque支持在前端和后端执行快速的插入和删除操作 2、随机访问&#xff…

Colorfy v3.26 — 修改版,超过2000种图片涂色

Colorfy 是一款适用于安卓设备的涂色书应用&#xff0c;提供了超过 2000 种不同的图片供用户涂色&#xff0c;包括动物、花卉、城市风景等。每日更新新图&#xff0c;支持各种颜色和绘画工具&#xff0c;如水彩笔、铅笔、细线笔等。用户可以放大绘画区域进行精细绘画。此版本由…

Transformer-LSTM神经网络多输入单输出回归预测的MATLAB实现

在现代机器学习和深度学习领域&#xff0c;处理多维时序数据的需求越来越多。在这些应用场景中&#xff0c;我们常常面临需要同时处理多个输入变量&#xff0c;并预测一个输出变量的任务。这类问题通常被称为多输入单输出回归问题。为了有效地应对这类问题&#xff0c;结合 Tra…

在浮躁的时代,保持冷静和沉稳,心怀勇气

在大家阅读之前&#xff0c;我真诚地祝愿大家在浮躁的世界中找到属于自己的宁静与力量&#xff0c;让每一天都充满希望和快乐。 在这个快节奏、信息爆炸的时代&#xff0c;我们每个人都像是被卷入了一个永不停歇的漩涡&#xff0c;不断地被各种声音和画面拉扯着。有时候&#x…

一篇搞懂classpath,resources

Classpath&#xff08;类路径&#xff09;在Java开发中是一个非常重要的概念&#xff0c;它指定了Java虚拟机&#xff08;JVM&#xff09;和Java编译器寻找类文件和包的位置。具体来说&#xff0c;Classpath告诉Java执行环境在哪些目录下可以找到所要执行的Java程序所需要的类或…

如何打造基于Java SpringBoot和Vue的医院门诊智能预约平台?四步实现高效就医流程,整合MySQL数据库,优化用户体验。

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

【#第三期实战营闯关作业##LMDeploy 量化部署进阶实践 】

今天学习了《LMDeploy 量化部署进阶实践》一课&#xff0c;&#xff0c;收获很大。以下是记录复现过程及截图&#xff1a; 创建一个名为lmdeploy的conda环境 创建成功后激活环境并安装0.5.3版本的lmdeploy及相关包。 相关包install成功 创建好的conda环境并启动InternLM2_5-7…

Java 入门指南:Java 并发编程 —— 线程安全问题与锁机制

线程安全的概念 线程安全可以简单理解为在多线程环境下&#xff0c;一个方法或实例在并发执行时能够保持数据的一致性和完整性&#xff0c;不会因为多个线程的交替执行而导致错误的结果。 线程不安全的原因 线程不安全通常由以下几个原因引起&#xff1a; 原子性问题&#x…