信令和协议设计
enum MessageCmdID {
// ...... 省略无关逻辑
CID_MSG_UNREAD_CNT_REQUEST = 775,
CID_MSG_UNREAD_CNT_RESPONSE = 776,
// ...... 省略无关逻辑
};
message IMUnreadMsgCntReq{
//cmd id: 0x0307
required uint32 user_id = 1;
optional bytes attach_data = 20;
}
message IMUnreadMsgCntRsp{
//cmd id: 0x0308
required uint32 user_id = 1;
required uint32 total_cnt = 2; // 多个人的未读消息
repeated IM.BaseDefine.UnreadInfo unreadinfo_list = 3;
optional bytes attach_data = 20;
}
message UnreadInfo{
required uint32 session_id = 1; // 会话ID
required SessionType session_type = 2; // 会话类型
required uint32 unread_cnt = 3; // 未读消息数量
required uint32 latest_msg_id = 4; // 最新的消息ID
required bytes latest_msg_data = 5; // 最新的消息
required MsgType latest_msg_type = 6; // 消息类型
required uint32 latest_msg_from_user_id = 7; //发送的用户id
}
流程图:
代码分析
msg_server收到CID_MSG_UNREAD_CNT_REQUEST后调用 CMsgConn::_HandleClientUnreadMsgCntRequest 函数
void CMsgConn::HandlePdu(CImPdu* pPdu)
{
// ...... 省略无关逻辑
switch (pPdu->GetCommandId()) {
// ...... 省略无关逻辑
case CID_MSG_UNREAD_CNT_REQUEST:
_HandleClientUnreadMsgCntRequest(pPdu );
break;
// ...... 省略无关逻辑
}
}
void CMsgConn::_HandleClientUnreadMsgCntRequest(CImPdu* pPdu)
{
log("HandleClientUnreadMsgCntReq, from_id=%u ", GetUserId());
IM::Message::IMUnreadMsgCntReq msg;
CHECK_PB_PARSE_MSG(msg.ParseFromArray(pPdu->GetBodyData(), pPdu->GetBodyLength()));
CDBServConn* pDBConn = get_db_serv_conn_for_login();
if (pDBConn) {
CDbAttachData attach(ATTACH_TYPE_HANDLE, m_handle, 0);
msg.set_user_id(GetUserId());
msg.set_attach_data(attach.GetBuffer(), attach.GetLength());
pPdu->SetPBMsg(&msg);
pDBConn->SendPdu(pPdu);
}
}
db_proxy_server收到CID_MSG_UNREAD_CNT_REQUEST后调用 DB_PROXY::getUnreadMsgCounter函数
值得注意的是,返回的未读消息里面包含每个会话的未读消息个数,消息类型,最后一条消息。
m_handler_map.insert(make_pair(uint32_t(CID_MSG_UNREAD_CNT_REQUEST), DB_PROXY::getUnreadMsgCounter));
void getUnreadMsgCounter(CImPdu* pPdu, uint32_t conn_uuid)
{
IM::Message::IMUnreadMsgCntReq msg;
IM::Message::IMUnreadMsgCntRsp msgResp;
if(msg.ParseFromArray(pPdu->GetBodyData(), pPdu->GetBodyLength()))
{
CImPdu* pPduResp = new CImPdu;
uint32_t nUserId = msg.user_id();
list<IM::BaseDefine::UnreadInfo> lsUnreadCount;
uint32_t nTotalCnt = 0;
// 从redis获取未读消息数量 和 从mysql获取最后一条未读消息
CMessageModel::getInstance()->getUnreadMsgCount(nUserId, nTotalCnt, lsUnreadCount);
CGroupMessageModel::getInstance()->getUnreadMsgCount(nUserId, nTotalCnt, lsUnreadCount);
msgResp.set_user_id(nUserId);
msgResp.set_total_cnt(nTotalCnt);
for(auto it= lsUnreadCount.begin(); it!=lsUnreadCount.end(); ++it)
{
IM::BaseDefine::UnreadInfo* pInfo = msgResp.add_unreadinfo_list();
pInfo->set_session_id(it->session_id());
pInfo->set_session_type(it->session_type());
pInfo->set_unread_cnt(it->unread_cnt());
pInfo->set_latest_msg_id(it->latest_msg_id());
pInfo->set_latest_msg_data(it->latest_msg_data());
pInfo->set_latest_msg_type(it->latest_msg_type());
pInfo->set_latest_msg_from_user_id(it->latest_msg_from_user_id());
}
log("userId=%d, unreadCnt=%u, totalCount=%u", nUserId, msgResp.unreadinfo_list_size(), nTotalCnt);
msgResp.set_attach_data(msg.attach_data());
pPduResp->SetPBMsg(&msgResp);
pPduResp->SetSeqNum(pPdu->GetSeqNum());
pPduResp->SetServiceId(IM::BaseDefine::SID_MSG);
pPduResp->SetCommandId(IM::BaseDefine::CID_MSG_UNREAD_CNT_RESPONSE);
CProxyConn::AddResponsePdu(conn_uuid, pPduResp);
}
else
{
log("parse pb failed");
}
}
void CMessageModel::getUnreadMsgCount(uint32_t nUserId, uint32_t &nTotalCnt, list<IM::BaseDefine::UnreadInfo>& lsUnreadCount)
{
// redis
CacheManager* pCacheManager = CacheManager::getInstance();
CacheConn* pCacheConn = pCacheManager->GetCacheConn("unread");
if (pCacheConn)
{
map<string, string> mapUnread;
string strKey = "unread_" + int2string(nUserId);
bool bRet = pCacheConn->hgetAll(strKey, mapUnread);
pCacheManager->RelCacheConn(pCacheConn);
if(bRet)
{
IM::BaseDefine::UnreadInfo cUnreadInfo;
for (auto it = mapUnread.begin(); it != mapUnread.end(); it++) {
cUnreadInfo.set_session_id(atoi(it->first.c_str()));
cUnreadInfo.set_unread_cnt(atoi(it->second.c_str()));
cUnreadInfo.set_session_type(IM::BaseDefine::SESSION_TYPE_SINGLE);
uint32_t nMsgId = 0;
string strMsgData;
IM::BaseDefine::MsgType nMsgType;
// 从mysql获取最后一条未读消息 mysql
getLastMsg(cUnreadInfo.session_id(), nUserId, nMsgId, strMsgData, nMsgType);
if(IM::BaseDefine::MsgType_IsValid(nMsgType))
{
cUnreadInfo.set_latest_msg_id(nMsgId);
cUnreadInfo.set_latest_msg_data(strMsgData);
cUnreadInfo.set_latest_msg_type(nMsgType);
cUnreadInfo.set_latest_msg_from_user_id(cUnreadInfo.session_id());
lsUnreadCount.push_back(cUnreadInfo);
nTotalCnt += cUnreadInfo.unread_cnt();
}
else
{
log("invalid msgType. userId=%u, peerId=%u, msgType=%u", nUserId, cUnreadInfo.session_id(), nMsgType);
}
}
}
else
{
log("hgetall %s failed!", strKey.c_str());
}
}
else
{
log("no cache connection for unread");
}
}
void CMessageModel::getLastMsg(uint32_t nFromId, uint32_t nToId, uint32_t& nMsgId, string& strMsgData, IM::BaseDefine::MsgType& nMsgType, uint32_t nStatus)
{
uint32_t nRelateId = CRelationModel::getInstance()->getRelationId(nFromId, nToId, false);
if (nRelateId != INVALID_VALUE)
{
CDBManager* pDBManager = CDBManager::getInstance();
// 读从库
CDBConn* pDBConn = pDBManager->GetDBConn("teamtalk_slave");
if (pDBConn)
{
string strTableName = "IMMessage_" + int2string(nRelateId % 8);
string strSql = "select msgId,type,content from " + strTableName + " force index (idx_relateId_status_created) where relateId= " + int2string(nRelateId) + " and status = 0 order by created desc, id desc limit 1";
CResultSet* pResultSet = pDBConn->ExecuteQuery(strSql.c_str());
if (pResultSet)
{
while (pResultSet->Next())
{
nMsgId = pResultSet->GetInt("msgId");
nMsgType = IM::BaseDefine::MsgType(pResultSet->GetInt("type"));
if (nMsgType == IM::BaseDefine::MSG_TYPE_SINGLE_AUDIO)
{
// "[语音]"加密后的字符串
strMsgData = strAudioEnc;
}
else
{
strMsgData = pResultSet->GetString("content");
}
}
delete pResultSet;
}
else
{
log("no result set: %s", strSql.c_str());
}
pDBManager->RelDBConn(pDBConn);
}
else
{
log("no db connection_slave");
}
}
else
{
log("no relation between %lu and %lu", nFromId, nToId);
}
}
db_proxy_server回复信令CID_MSG_UNREAD_CNT_RESPONSE给msg_server,调用CDBServConn::_HandleUnreadMsgCountResponse
void CDBServConn::_HandleUnreadMsgCountResponse(CImPdu* pPdu)
{
IM::Message::IMUnreadMsgCntRsp msg;
CHECK_PB_PARSE_MSG(msg.ParseFromArray(pPdu->GetBodyData(), pPdu->GetBodyLength()));
uint32_t user_id = msg.user_id();
uint32_t total_cnt = msg.total_cnt();
uint32_t user_unread_cnt = msg.unreadinfo_list_size();
CDbAttachData attach_data((uchar_t*)msg.attach_data().c_str(), msg.attach_data().length());
uint32_t handle = attach_data.GetHandle();
log("HandleUnreadMsgCntResp, userId=%u, total_cnt=%u, user_unread_cnt=%u.", user_id,
total_cnt, user_unread_cnt);
CMsgConn* pMsgConn = CImUserManager::GetInstance()->GetMsgConnByHandle(user_id, handle);
if (pMsgConn && pMsgConn->IsOpen()) {
msg.clear_attach_data();
pPdu->SetPBMsg(&msg);
pMsgConn->SendPdu(pPdu);
}
}