聊天服务器——全部业务

全部业务概述

全部业务:

心跳包

注册

登录

获取好友列表

查找用户

加好友

更新用户信息

修改密码

创建群

获取指定群成员信息

聊天信息

群发信息

数据包

数据包的格式是:业务id加上序列号m_seq。

心跳包业务

心跳包业务。主要是看对方还在不在线,所以不需要写什么额外的数据,就是写一个简单的数据包。

数据包内容就是msg_type_heartbeart+m_seq,然后发送过去。

std::string outbuf;
yt::BinaryWriteStream3 writeStream(&outbuf);
writeStream.Write(msg_type_heartbeart);
writeStream.Write(m_seq);
std::string dummy;
writeStream.Write(dummy.c_str(), dummy.length());
writeStream.Flush();
​
LOG_INFO << "Response to client: cmd=1000" << ", sessionId=" << m_id;
​
Send(outbuf);

注册业务

注册业务,先是解析发来的注册数据是否符合json数据格式,如果符合,就进一步注册。如果不符合,就不进行注册了。

然后根据注册的用户名,在UserManager里进行对所有用户进行查找,如果找到了相同的用户名。那么就终止注册,并返回已注册的信息;如果没有找到相同的用户名,那么就在UserManager中添加用户了,并发送成功注册的信息。

void ClientSession::OnRegisterResponse(const std::string& data, const std::shared_ptr<TcpConnection>& conn)
{
    //{ "user": "13917043329", "nickname" : "balloon", "password" : "123" }
    Json::Reader JsonReader;
    Json::Value JsonRoot;
    if (!JsonReader.parse(data, JsonRoot))
    {
        LOG_WARN << "invalid json: " << data << ", sessionId = " << m_id << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    if (!JsonRoot["username"].isString() || !JsonRoot["nickname"].isString() || !JsonRoot["password"].isString())
    {
        LOG_WARN << "invalid json: " << data << ", sessionId = " << m_id << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    User u;
    u.username = JsonRoot["username"].asString();
    u.nickname = JsonRoot["nickname"].asString();
    u.password = JsonRoot["password"].asString();
​
    std::string retData;
    User cachedUser;
    cachedUser.userid = 0;
    Singleton<UserManager>::Instance().GetUserInfoByUsername(u.username, cachedUser);
    if (cachedUser.userid != 0)
        retData = "{\"code\": 101, \"msg\": \"registered already\"}";
    else
    {
        if (!Singleton<UserManager>::Instance().AddUser(u))
            retData = "{\"code\": 100, \"msg\": \"register failed\"}";
        else
            retData = "{\"code\": 0, \"msg\": \"ok\"}";
    }
    
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_register);
    writeStream.Write(m_seq);   
    writeStream.Write(retData.c_str(), retData.length());
    writeStream.Flush();
​
    LOG_INFO << "Response to client: cmd=msg_type_register" << ", userid=" << u.userid << ", data=" << retData;
​
    Send(outbuf);
}

登录业务

登录业务先检查发送过来的数据是否符合json格式,如果不符合,就记录在日志上,然后直接返回。

如果符合json格式,就解析json格式,并解析出对应的用户名和密码。

然后在UserManager中去查询用户名,如果用户名不存在,就返回“未注册的”消息

如果用户名存在,就判断密码一不一样。如果密码不一样,就返回用户名或密码错误的消息

如果密码一样了,就返回ok的消息,并附带上各种用户信息。

登录成功后,要推送用户缓存的通知消息和聊天消息。同时给其他用户推送上线的消息

推送缓存消息是从<MsgCacheManager>中拿到缓存,然后再发送出去。

推送用户上线消息:先获得该用户的好友,如果这些好友在线的话。就获得好友对应的ClientSession,然后发送用户上线状态。

void ClientSession::OnLoginResponse(const std::string& data, const std::shared_ptr<TcpConnection>& conn)
{
    //{"username": "13917043329", "password": "123", "clienttype": 1, "status": 1}
    Json::Reader JsonReader;
    Json::Value JsonRoot;
    if (!JsonReader.parse(data, JsonRoot))
    {
        LOG_WARN << "invalid json: " << data << ", sessionId = " << m_id  << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    if (!JsonRoot["username"].isString() || !JsonRoot["password"].isString() || !JsonRoot["clienttype"].isInt() || !JsonRoot["status"].isInt())
    {
        LOG_WARN << "invalid json: " << data << ", sessionId = " << m_id << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    string username = JsonRoot["username"].asString();
    string password = JsonRoot["password"].asString();
    std::ostringstream os;
    User cachedUser;
    cachedUser.userid = 0;
    Singleton<UserManager>::Instance().GetUserInfoByUsername(username, cachedUser);
    if (cachedUser.userid == 0)
    {
        //TODO: 这些硬编码的字符应该统一放到某个地方统一管理
        os << "{\"code\": 102, \"msg\": \"not registered\"}";
    }
    else
    {
        if (cachedUser.password != password)
            os << "{\"code\": 103, \"msg\": \"incorrect password\"}";
        else
        {
            //记录用户信息
            m_userinfo.userid = cachedUser.userid;
            m_userinfo.username = username;
            m_userinfo.nickname = cachedUser.nickname;
            m_userinfo.password = password;
            m_userinfo.clienttype = JsonRoot["clienttype"].asInt();
            m_userinfo.status = JsonRoot["status"].asInt();
​
            os << "{\"code\": 0, \"msg\": \"ok\", \"userid\": " << m_userinfo.userid << ",\"username\":\"" << cachedUser.username << "\", \"nickname\":\"" 
               << cachedUser.nickname << "\", \"facetype\": " << cachedUser.facetype << ", \"customface\":\"" << cachedUser.customface << "\", \"gender\":" << cachedUser.gender
               << ", \"birthday\":" << cachedUser.birthday << ", \"signature\":\"" << cachedUser.signature << "\", \"address\": \"" << cachedUser.address
               << "\", \"phonenumber\": \"" << cachedUser.phonenumber << "\", \"mail\":\"" << cachedUser.mail << "\"}";
        }
    }
   
    //登录信息应答
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_login);
    writeStream.Write(m_seq);  
    writeStream.Write(os.str().c_str(), os.str().length());
    writeStream.Flush();
​
    LOG_INFO << "Response to client: cmd=msg_type_login, data=" << os.str() << ", userid=" << m_userinfo.userid;
    
    Send(outbuf);
​
    //推送通知消息
    std::list<NotifyMsgCache> listNotifyCache;
    Singleton<MsgCacheManager>::Instance().GetNotifyMsgCache(m_userinfo.userid, listNotifyCache);
    for (const auto &iter : listNotifyCache)
    {
        Send(iter.notifymsg);
    }
​
    //推送聊天消息
    std::list<ChatMsgCache> listChatCache;
    Singleton<MsgCacheManager>::Instance().GetChatMsgCache(m_userinfo.userid, listChatCache);
    for (const auto &iter : listChatCache)
    {
        Send(iter.chatmsg);
    }
​
    //给其他用户推送上线消息
    std::list<User> friends;
    Singleton<UserManager>::Instance().GetFriendInfoByUserId(m_userinfo.userid, friends);
    IMServer& imserver = Singleton<IMServer>::Instance();
    for (const auto& iter : friends)
    {
        //先看目标用户是否在线
        std::shared_ptr<ClientSession> targetSession;
        imserver.GetSessionByUserId(targetSession, iter.userid);
        if (targetSession)
            targetSession->SendUserStatusChangeMsg(m_userinfo.userid, 1);
    }
}

获取好友列表

根据用户id在UserManager中获得好友列表,然后把好友列表里每个用户的信息都转化成数据包,然后发送过去。

void ClientSession::OnGetFriendListResponse(const std::shared_ptr<TcpConnection>& conn)
{
    std::list<User> friends;
    Singleton<UserManager>::Instance().GetFriendInfoByUserId(m_userinfo.userid, friends);
    std::string strUserInfo;
    bool userOnline = false;
    IMServer& imserver = Singleton<IMServer>::Instance();
    for (const auto& iter : friends)
    {   
        userOnline = imserver.IsUserSessionExsit(iter.userid);
        /*
        {"code": 0, "msg": "ok", "userinfo":[{"userid": 1,"username":"qqq, 
        "nickname":"qqq, "facetype": 0, "customface":"", "gender":0, "birthday":19900101, 
        "signature":", "address": "", "phonenumber": "", "mail":", "clienttype": 1, "status":1"]}
        */
        ostringstream osSingleUserInfo;
        osSingleUserInfo << "{\"userid\": " << iter.userid << ",\"username\":\"" << iter.username << "\", \"nickname\":\"" << iter.nickname
                         << "\", \"facetype\": " << iter.facetype << ", \"customface\":\"" << iter.customface << "\", \"gender\":" << iter.gender
                         << ", \"birthday\":" << iter.birthday << ", \"signature\":\"" << iter.signature << "\", \"address\": \"" << iter.address
                         << "\", \"phonenumber\": \"" << iter.phonenumber << "\", \"mail\":\"" << iter.mail << "\", \"clienttype\": 1, \"status\":"
                         << (userOnline ? 1 : 0) << "}";
​
        strUserInfo += osSingleUserInfo.str();
        strUserInfo += ",";
    }
    //去掉最后多余的逗号
    strUserInfo = strUserInfo.substr(0, strUserInfo.length() - 1);
    std::ostringstream os;
    os << "{\"code\": 0, \"msg\": \"ok\", \"userinfo\":[" << strUserInfo << "]}";
​
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_getofriendlist);
    writeStream.Write(m_seq);
    writeStream.Write(os.str().c_str(), os.str().length());
    writeStream.Flush();
​
    LOG_INFO << "Response to client: cmd=msg_type_getofriendlist, data=" << os.str() << ", userid=" << m_userinfo.userid;
​
    Send(outbuf);
}

查找用户业务

解析json格式跟前面是一样的

然后在UserManager里去查询是否有这个用户名,如果没有这个用户,就返回查询失败。如果有这个用户,就返回用户的信息。

void ClientSession::OnFindUserResponse(const std::string& data, const std::shared_ptr<TcpConnection>& conn)
{
    //{ "type": 1, "username" : "zhangyl" }
    Json::Reader JsonReader;
    Json::Value JsonRoot;
    if (!JsonReader.parse(data, JsonRoot))
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    if (!JsonRoot["type"].isInt() || !JsonRoot["username"].isString())
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    string retData;
    //TODO: 目前只支持查找单个用户
    string username = JsonRoot["username"].asString();
    User cachedUser;
    if (!Singleton<UserManager>::Instance().GetUserInfoByUsername(username, cachedUser))
        retData = "{ \"code\": 0, \"msg\": \"ok\", \"userinfo\": [] }";
    else
    {
        //TODO: 用户比较多的时候,应该使用动态string
        char szUserInfo[256] = { 0 };
        snprintf(szUserInfo, 256, "{ \"code\": 0, \"msg\": \"ok\", \"userinfo\": [{\"userid\": %d, \"username\": \"%s\", \"nickname\": \"%s\", \"facetype\":%d}] }", cachedUser.userid, cachedUser.username.c_str(), cachedUser.nickname.c_str(), cachedUser.facetype);
        retData = szUserInfo;
    }
​
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_finduser);
    writeStream.Write(m_seq);
    writeStream.Write(retData.c_str(), retData.length());
    writeStream.Flush();
​
    LOG_INFO << "Response to client: cmd=msg_type_finduser, data=" << retData << ", userid=" << m_userinfo.userid;
​
    Send(outbuf);
}

操作好友反应

从json格式的数据里主要提前两个数据,第一个是targetuserid,第二个是type。targetuserid是被操作的userid,type是操作类型。

(1)如果 targetUserid >= GROUPID_BOUBDARY,就说明被操作的对象是群,而不是用户。比如加群或者退群。

如果type类型是4,则是退群,调用DeleteFriend(conn, targetUserid);

否则是加群 OnAddGroupResponse(targetUserid, conn);

(2)接下来就是targetUserid < GROUPID_BOUBDARY,就表示操作的对象是用户,而不是群。

如果type是4,就是删好友,调用DeleteFriend(conn, targetUserid);

type是1,就是加好友

type是3,就是应答加好友。

应答加好友,就会去建立两个用户之间的关系。提示当前用户加好友成功,提示对方加好友成功。

如果对方不在,就把这个消息先缓存起来。

void ClientSession::OnOperateFriendResponse(const std::string& data, const std::shared_ptr<TcpConnection>& conn)
{
    Json::Reader JsonReader;
    Json::Value JsonRoot;
    if (!JsonReader.parse(data, JsonRoot))
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    if (!JsonRoot["type"].isInt() || !JsonRoot["userid"].isInt())
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    int type = JsonRoot["type"].asInt();
    int32_t targetUserid = JsonRoot["userid"].asInt();
    if (targetUserid >= GROUPID_BOUBDARY)
    {
        if (type == 4)
        {
            //退群
            DeleteFriend(conn, targetUserid);
            return;
        }
​
        //加群直接同意
        OnAddGroupResponse(targetUserid, conn);
        return;
    }
​
    char szData[256] = { 0 };
    //删除好友
    if (type == 4)
    {
        DeleteFriend(conn, targetUserid);
        return;
    }
    //发出加好友申请
    if (type == 1)
    {
        //{"userid": 9, "type": 1, }        
        snprintf(szData, 256, "{\"userid\":%d, \"type\":2, \"username\": \"%s\"}", m_userinfo.userid, m_userinfo.username.c_str());
    }
    //应答加好友
    else if (type == 3)
    {
        if (!JsonRoot["accept"].isInt())
        {
            LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << "client: " << conn->peerAddress().toIpPort();
            return;
        }
​
        int accept = JsonRoot["accept"].asInt();
        //接受加好友申请后,建立好友关系
        if (accept == 1)
        {
            int smallid = m_userinfo.userid;
            int greatid = targetUserid;
            //数据库里面互为好友的两个人id,小者在先,大者在后
            if (smallid > greatid)
            {
                smallid = targetUserid;
                greatid = m_userinfo.userid;
            }
​
            if (!Singleton<UserManager>::Instance().MakeFriendRelationship(smallid, greatid))
            {
                LOG_ERROR << "make relationship error: " << data << ", userid: " << m_userinfo.userid << "client: " << conn->peerAddress().toIpPort();
                return;
            }
        }
​
        //{ "userid": 9, "type" : 3, "userid" : 9, "username" : "xxx", "accept" : 1 }
        snprintf(szData, 256, "{\"userid\": %d, \"type\": 3, \"username\": \"%s\", \"accept\": %d}", m_userinfo.userid, m_userinfo.username.c_str(), accept);
​
        //提示自己当前用户加好友成功
        User targetUser;
        if (!Singleton<UserManager>::Instance().GetUserInfoByUserId(targetUserid, targetUser))
        {
            LOG_ERROR << "Get Userinfo by id error, targetuserid: " << targetUserid << ", userid: " << m_userinfo.userid << ", data: "<< data << ", client: " << conn->peerAddress().toIpPort();
            return;
        }
        char szSelfData[256] = { 0 };
        snprintf(szSelfData, 256, "{\"userid\": %d, \"type\": 3, \"username\": \"%s\", \"accept\": %d}", targetUser.userid, targetUser.username.c_str(), accept);
        std::string outbufx;
        yt::BinaryWriteStream3 writeStream(&outbufx);
        writeStream.Write(msg_type_operatefriend);
        writeStream.Write(m_seq);
        writeStream.Write(szSelfData, strlen(szSelfData));
        writeStream.Flush();
​
        Send(outbufx);
        LOG_INFO << "Response to client: cmd=msg_type_addfriend, data=" << szSelfData << ", userid=" << m_userinfo.userid;
    }
​
    //提示对方加好友成功
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_operatefriend);
    writeStream.Write(m_seq);
    writeStream.Write(szData, strlen(szData));
    writeStream.Flush();
​
    //先看目标用户是否在线
    std::shared_ptr<ClientSession> targetSession;
    Singleton<IMServer>::Instance().GetSessionByUserId(targetSession, targetUserid);
    //目标用户不在线,缓存这个消息
    if (!targetSession)
    {
        Singleton<MsgCacheManager>::Instance().AddNotifyMsgCache(targetUserid, outbuf);
        LOG_INFO << "userid: " << targetUserid << " is not online, cache notify msg, msg: " << outbuf;
        return;
    }
​
    targetSession->Send(outbuf);
​
    LOG_INFO << "Response to client: cmd=msg_type_addfriend, data=" << data << ", userid=" << targetUserid;
}

删好友

删好友,先把两个人从好友关系表中剔除Singleton<UserManager>::Instance().ReleaseFriendRelationship

然后给主动删除方发送消息。

然后给被删除方发送删除好友消息,如果用户在线,就推送这个消息;否则先把消息缓存起来

如果是群的话,就给其他群成员推送群成员变动消息,如果是群成员不在线,那么就先缓存起来。

void ClientSession::DeleteFriend(const std::shared_ptr<TcpConnection>& conn, int32_t friendid)
{
    int32_t smallerid = friendid;
    int32_t greaterid = m_userinfo.userid;
    if (smallerid > greaterid)
    {
        smallerid = m_userinfo.userid;
        greaterid = friendid;
    }
​
    if (!Singleton<UserManager>::Instance().ReleaseFriendRelationship(smallerid, greaterid))
    {
        LOG_ERROR << "Delete friend error, friendid: " << friendid << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    User cachedUser;
    if (!Singleton<UserManager>::Instance().GetUserInfoByUserId(friendid, cachedUser))
    {
        LOG_ERROR << "Delete friend - Get user error, friendid: " << friendid << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    char szData[256] = { 0 };
    //发给主动删除的一方
    //{"userid": 9, "type": 1, }        
    snprintf(szData, 256, "{\"userid\":%d, \"type\":5, \"username\": \"%s\"}", friendid, cachedUser.username.c_str());
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_operatefriend);
    writeStream.Write(m_seq);
    writeStream.Write(szData, strlen(szData));
    writeStream.Flush();
​
    Send(outbuf);
​
    LOG_INFO << "Send to client: cmd=msg_type_operatefriend, data=" << szData << ", userid=" << m_userinfo.userid;
​
    //发给被删除的一方
    //删除好友消息
    if (friendid < GROUPID_BOUBDARY)
    {
        outbuf.clear();
        //先看目标用户是否在线
        std::shared_ptr<ClientSession> targetSession;
        Singleton<IMServer>::Instance().GetSessionByUserId(targetSession, friendid);
        //仅给在线用户推送这个消息
        if (targetSession)
        {
            memset(szData, 0, sizeof(szData));
            snprintf(szData, 256, "{\"userid\":%d, \"type\":5, \"username\": \"%s\"}", m_userinfo.userid, m_userinfo.username.c_str());
            outbuf.clear();
            writeStream.Clear();
            writeStream.Write(msg_type_operatefriend);
            writeStream.Write(m_seq);
            writeStream.Write(szData, strlen(szData));
            writeStream.Flush();
​
            targetSession->Send(outbuf);
​
            LOG_INFO << "Send to client: cmd=msg_type_operatefriend, data=" << szData << ", userid=" << friendid;
        }
​
        return;
    }
    
    //退群消息
    //给其他在线群成员推送群信息发生变化的消息
    std::list<User> friends;
    Singleton<UserManager>::Instance().GetFriendInfoByUserId(friendid, friends);
    IMServer& imserver = Singleton<IMServer>::Instance();
    for (const auto& iter : friends)
    {
        //先看目标用户是否在线
        std::shared_ptr<ClientSession> targetSession;
        imserver.GetSessionByUserId(targetSession, iter.userid);
        if (targetSession)
            targetSession->SendUserStatusChangeMsg(friendid, 3);
    }
​
}

加群

先给群id和用户id建立关系MakeFriendRelationship

然后给用户发送消息

给群里的其他成员发送群信息变化消息

void ClientSession::OnAddGroupResponse(int32_t groupId, const std::shared_ptr<TcpConnection>& conn)
{
    if (!Singleton<UserManager>::Instance().MakeFriendRelationship(m_userinfo.userid, groupId))
    {
        LOG_ERROR << "make relationship error, groupId: " << groupId << ", userid: " << m_userinfo.userid << "client: " << conn->peerAddress().toIpPort();
        return;
    }
    
    User groupUser;
    if (!Singleton<UserManager>::Instance().GetUserInfoByUserId(groupId, groupUser))
    {
        LOG_ERROR << "Get group info by id error, targetuserid: " << groupId << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
    char szSelfData[256] = { 0 };
    snprintf(szSelfData, 256, "{\"userid\": %d, \"type\": 3, \"username\": \"%s\", \"accept\": 3}", groupUser.userid, groupUser.username.c_str());
    std::string outbufx;
    yt::BinaryWriteStream3 writeStream(&outbufx);
    writeStream.Write(msg_type_operatefriend);
    writeStream.Write(m_seq);
    writeStream.Write(szSelfData, strlen(szSelfData));
    writeStream.Flush();
​
    Send(outbufx);
    LOG_INFO << "Response to client: cmd=msg_type_addfriend, data=" << szSelfData << ", userid=" << m_userinfo.userid;
​
    //给其他在线群成员推送群信息发生变化的消息
    std::list<User> friends;
    Singleton<UserManager>::Instance().GetFriendInfoByUserId(groupId, friends);
    IMServer& imserver = Singleton<IMServer>::Instance();
    for (const auto& iter : friends)
    {
        //先看目标用户是否在线
        std::shared_ptr<ClientSession> targetSession;
        imserver.GetSessionByUserId(targetSession, iter.userid);
        if (targetSession)
            targetSession->SendUserStatusChangeMsg(groupId, 3);
    }
}

更新用户信息业务

从json格式中解析数据,解析更新后用户的信息,然后把用户的信息更新到UserManager。

应答客户端,同时给其他在线好友推送个人信息发送改变消息

void ClientSession::OnUpdateUserInfoResponse(const std::string& data, const std::shared_ptr<TcpConnection>& conn)
{
    Json::Reader JsonReader;
    Json::Value JsonRoot;
    if (!JsonReader.parse(data, JsonRoot))
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    if (!JsonRoot["nickname"].isString() || !JsonRoot["facetype"].isInt() || 
        !JsonRoot["customface"].isString() || !JsonRoot["gender"].isInt() || 
        !JsonRoot["birthday"].isInt() || !JsonRoot["signature"].isString() || 
        !JsonRoot["address"].isString() || !JsonRoot["phonenumber"].isString() || 
        !JsonRoot["mail"].isString())
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    User newuserinfo;
    newuserinfo.nickname = JsonRoot["nickname"].asString();
    newuserinfo.facetype = JsonRoot["facetype"].asInt();
    newuserinfo.customface = JsonRoot["customface"].asString();
    newuserinfo.gender = JsonRoot["gender"].asInt();
    newuserinfo.birthday = JsonRoot["birthday"].asInt();
    newuserinfo.signature = JsonRoot["signature"].asString();
    newuserinfo.address = JsonRoot["address"].asString();
    newuserinfo.phonenumber = JsonRoot["phonenumber"].asString();
    newuserinfo.mail = JsonRoot["mail"].asString();
    
    ostringstream retdata;
    ostringstream currentuserinfo;
    if (!Singleton<UserManager>::Instance().UpdateUserInfo(m_userinfo.userid, newuserinfo))
    {
        retdata << "{ \"code\": 104, \"msg\": \"update user info failed\" }";
    }
    else
    {
        /*
        { "code": 0, "msg" : "ok", "userid" : 2, "username" : "xxxx", 
         "nickname":"zzz", "facetype" : 26, "customface" : "", "gender" : 0, "birthday" : 19900101, 
         "signature" : "xxxx", "address": "", "phonenumber": "", "mail":""}
        */
        currentuserinfo << "\"userid\": " << m_userinfo.userid << ",\"username\":\"" << m_userinfo.username
                        << "\", \"nickname\":\"" << newuserinfo.nickname
                        << "\", \"facetype\": " << newuserinfo.facetype << ", \"customface\":\"" << newuserinfo.customface
                        << "\", \"gender\":" << newuserinfo.gender
                        << ", \"birthday\":" << newuserinfo.birthday << ", \"signature\":\"" << newuserinfo.signature
                        << "\", \"address\": \"" << newuserinfo.address
                        << "\", \"phonenumber\": \"" << newuserinfo.phonenumber << "\", \"mail\":\""
                        << newuserinfo.mail;
        retdata << "{\"code\": 0, \"msg\": \"ok\"," << currentuserinfo.str()  << "\"}";
    }
​
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_updateuserinfo);
    writeStream.Write(m_seq);
    writeStream.Write(retdata.str().c_str(), retdata.str().length());
    writeStream.Flush();
​
    //应答客户端
    Send(outbuf);
​
    LOG_INFO << "Response to client: cmd=msg_type_updateuserinfo, data=" << retdata.str() << ", userid=" << m_userinfo.userid;
​
    //给其他在线好友推送个人信息发生改变消息
    std::list<User> friends;
    Singleton<UserManager>::Instance().GetFriendInfoByUserId(m_userinfo.userid, friends);
    IMServer& imserver = Singleton<IMServer>::Instance();
    for (const auto& iter : friends)
    {
        //先看目标用户是否在线
        std::shared_ptr<ClientSession> targetSession;
        imserver.GetSessionByUserId(targetSession, iter.userid);
        if (targetSession)
            targetSession->SendUserStatusChangeMsg(m_userinfo.userid, 3);
    }
}

改密码的业务

从json格式中解析出新密码和旧密码。先比较旧密码是否一致,如果旧密码一样才可以改新密码。如果旧密码不一样,那么就发送密码错误给客户端。只有旧密码正确才可以改新密码

Singleton<UserManager>::Instance().ModifyUserPassword(m_userinfo.userid, newPass)

void ClientSession::OnModifyPasswordResponse(const std::string& data, const std::shared_ptr<TcpConnection>& conn)
{
    Json::Reader JsonReader;
    Json::Value JsonRoot;
    if (!JsonReader.parse(data, JsonRoot))
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    if (!JsonRoot["oldpassword"].isString() || !JsonRoot["newpassword"].isString())
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    string oldpass = JsonRoot["oldpassword"].asString();
    string newPass = JsonRoot["newpassword"].asString();
​
    string retdata;
    User cachedUser;
    if (!Singleton<UserManager>::Instance().GetUserInfoByUserId(m_userinfo.userid, cachedUser))
    {
        LOG_ERROR << "get userinfo error, userid: " << m_userinfo.userid << ", data: " << data << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    if (cachedUser.password != oldpass)
    {
        retdata = "{\"code\": 103, \"msg\": \"incorrect old password\"}";
    }
    else
    {       
        if (!Singleton<UserManager>::Instance().ModifyUserPassword(m_userinfo.userid, newPass))
        {
            retdata = "{\"code\": 105, \"msg\": \"modify password error\"}";
            LOG_ERROR << "modify password error, userid: " << m_userinfo.userid << ", data: " << data << ", client: " << conn->peerAddress().toIpPort();
        }
        else
            retdata = "{\"code\": 0, \"msg\": \"ok\"}";
    }
​
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_modifypassword);
    writeStream.Write(m_seq);
    writeStream.Write(retdata.c_str(), retdata.length());
    writeStream.Flush();
​
    //应答客户端
    Send(outbuf);
​
    LOG_INFO << "Response to client: cmd=msg_type_modifypassword, data=" << data << ", userid=" << m_userinfo.userid;
}

建群业务

从json格式中解析出群名

然后调用UserManager来添加群,AddGroup

创建成功后,该用户自动加群MakeFriendRelationship

之后应答两次。第一次是创建群成功,第二次是加群成功

void ClientSession::OnCreateGroupResponse(const std::string& data, const std::shared_ptr<TcpConnection>& conn)
{
    Json::Reader JsonReader;
    Json::Value JsonRoot;
    if (!JsonReader.parse(data, JsonRoot))
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    if (!JsonRoot["groupname"].isString())
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    ostringstream retdata;
    string groupname = JsonRoot["groupname"].asString();
    int32_t groupid;
    if (!Singleton<UserManager>::Instance().AddGroup(groupname.c_str(), m_userinfo.userid, groupid))
    {
        LOG_WARN << "Add group error, data: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        retdata << "{ \"code\": 106, \"msg\" : \"create group error\"}";
    }
    else
    {
        retdata << "{\"code\": 0, \"msg\": \"ok\", \"groupid\":" << groupid << ", \"groupname\": \"" << groupname << "\"}";
    }
​
    //创建成功以后该用户自动加群
    if (!Singleton<UserManager>::Instance().MakeFriendRelationship(m_userinfo.userid, groupid))
    {
        LOG_ERROR << "join in group, errordata: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_creategroup);
    writeStream.Write(m_seq);
    writeStream.Write(retdata.str().c_str(), retdata.str().length());
    writeStream.Flush();
​
    //应答客户端,建群成功
    Send(outbuf);
​
    LOG_INFO << "Response to client: cmd=msg_type_creategroup, data=" << retdata.str() << ", userid=" << m_userinfo.userid;
​
    //应答客户端,成功加群
    {
        char szSelfData[256] = { 0 };
        snprintf(szSelfData, 256, "{\"userid\": %d, \"type\": 3, \"username\": \"%s\", \"accept\": 1}", groupid, groupname.c_str());
        std::string outbufx;
        yt::BinaryWriteStream3 writeStream(&outbufx);
        writeStream.Write(msg_type_operatefriend);
        writeStream.Write(m_seq);
        writeStream.Write(szSelfData, strlen(szSelfData));
        writeStream.Flush();
​
        Send(outbufx);
        LOG_INFO << "Response to client: cmd=msg_type_addfriend, data=" << szSelfData << ", userid=" << m_userinfo.userid;
    }
}

获得群成员业务

其实获得群成员业务和获得好友列表业务是一样的。因为群就是userid

void ClientSession::OnGetGroupMembersResponse(const std::string& data, const std::shared_ptr<TcpConnection>& conn)
{
    //{"groupid": 群id}
    Json::Reader JsonReader;
    Json::Value JsonRoot;
    if (!JsonReader.parse(data, JsonRoot))
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    if (!JsonRoot["groupid"].isInt())
    {
        LOG_WARN << "invalid json: " << data << ", userid: " << m_userinfo.userid << ", client: " << conn->peerAddress().toIpPort();
        return;
    }
​
    int32_t groupid = JsonRoot["groupid"].asInt();
    
    std::list<User> friends;
    Singleton<UserManager>::Instance().GetFriendInfoByUserId(groupid, friends);
    std::string strUserInfo;
    bool userOnline = false;
    IMServer& imserver = Singleton<IMServer>::Instance();
    for (const auto& iter : friends)
    {
        userOnline = imserver.IsUserSessionExsit(iter.userid);
        /*
        {"code": 0, "msg": "ok", "members":[{"userid": 1,"username":"qqq,
        "nickname":"qqq, "facetype": 0, "customface":"", "gender":0, "birthday":19900101,
        "signature":", "address": "", "phonenumber": "", "mail":", "clienttype": 1, "status":1"]}
        */
        ostringstream osSingleUserInfo;
        osSingleUserInfo << "{\"userid\": " << iter.userid << ",\"username\":\"" << iter.username << "\", \"nickname\":\"" << iter.nickname
            << "\", \"facetype\": " << iter.facetype << ", \"customface\":\"" << iter.customface << "\", \"gender\":" << iter.gender
            << ", \"birthday\":" << iter.birthday << ", \"signature\":\"" << iter.signature << "\", \"address\": \"" << iter.address
            << "\", \"phonenumber\": \"" << iter.phonenumber << "\", \"mail\":\"" << iter.mail << "\", \"clienttype\": 1, \"status\":"
            << (userOnline ? 1 : 0) << "}";
​
        strUserInfo += osSingleUserInfo.str();
        strUserInfo += ",";
    }
    //去掉最后多余的逗号
    strUserInfo = strUserInfo.substr(0, strUserInfo.length() - 1);
    std::ostringstream os;
    os << "{\"code\": 0, \"msg\": \"ok\", \"groupid\": " << groupid << ", \"members\":[" << strUserInfo << "]}";
​
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_getgroupmembers);
    writeStream.Write(m_seq);
    writeStream.Write(os.str().c_str(), os.str().length());
    writeStream.Flush();
​
    LOG_INFO << "Response to client: cmd=msg_type_getgroupmembers, data=" << os.str() << ", userid=" << m_userinfo.userid;
​
    Send(outbuf);
}

发送用户状态业务

void ClientSession::SendUserStatusChangeMsg(int32_t userid, int type)
{
    string data; 
    //用户上线
    if (type == 1)
    {
        data = "{\"type\": 1, \"onlinestatus\": 1}";
    }
    //用户下线
    else if (type == 2)
    {
        data = "{\"type\": 2, \"onlinestatus\": 0}";
    }
    //个人昵称、头像、签名等信息更改
    else if (type == 3)
    {
        data = "{\"type\": 3}";
    }
​
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_userstatuschange);
    writeStream.Write(m_seq);
    writeStream.Write(data.c_str(), data.length());
    writeStream.Write(userid);
    writeStream.Flush();
​
    Send(outbuf);
​
    LOG_INFO << "Send to client: cmd=msg_type_userstatuschange, data=" << data << ", userid=" << m_userinfo.userid;
}

发送聊天消息业务

先把消息写入到记录里,写入到数据库里。

(1)如果是单聊消息,targetid < GROUPID_BOUBDARY

如果接收方不在,那么先看看目标用户是否在线,如果目标用户不在线,那么就缓存这个消息

如果在线,那么就把目标用户的clientSession调出来,然后发送消息

(2)如果是群聊消息,就把群里的所有人调出来,然后看看目标是否在线,不在线就缓存消息,如果在线,就调出这个用户的clientSession,然后发送消息。

void ClientSession::OnChatResponse(int32_t targetid, const std::string& data, const std::shared_ptr<TcpConnection>& conn)
{
    std::string outbuf;
    yt::BinaryWriteStream3 writeStream(&outbuf);
    writeStream.Write(msg_type_chat);
    writeStream.Write(m_seq);
    writeStream.Write(data.c_str(), data.length());
    //消息发送者
    writeStream.Write(m_userinfo.userid);
    //消息接受者
    writeStream.Write(targetid);
    writeStream.Flush();
​
    UserManager& userMgr = Singleton<UserManager>::Instance();
    //写入消息记录
    if (!userMgr.SaveChatMsgToDb(m_userinfo.userid, targetid, data))
    {
        LOG_ERROR << "Write chat msg to db error, , senderid = " << m_userinfo.userid << ", targetid = " << targetid << ", chatmsg:" << data;
    }
​
    IMServer& imserver = Singleton<IMServer>::Instance();
    MsgCacheManager& msgCacheMgr = Singleton<MsgCacheManager>::Instance();
    //单聊消息
    if (targetid < GROUPID_BOUBDARY)
    {
        //先看目标用户是否在线
        std::shared_ptr<ClientSession> targetSession;
        imserver.GetSessionByUserId(targetSession, targetid);
        //目标用户不在线,缓存这个消息
        if (!targetSession)
        {
            msgCacheMgr.AddChatMsgCache(targetid, outbuf);
            return;
        }
​
        targetSession->Send(outbuf);
        return;
    }
​
    //群聊消息
    std::list<User> friends;
    userMgr.GetFriendInfoByUserId(targetid, friends);
    std::string strUserInfo;
    bool userOnline = false;
    for (const auto& iter : friends)
    {
        //先看目标用户是否在线
        std::shared_ptr<ClientSession> targetSession;
        imserver.GetSessionByUserId(targetSession, iter.userid);
        //目标用户不在线,缓存这个消息
        if (!targetSession)
        {
            msgCacheMgr.AddChatMsgCache(iter.userid, outbuf);
           continue;
        }
​
        targetSession->Send(outbuf);
    }
    
}
  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值