集群聊天服务器-一对一聊天

shilinkun
2022-05-11 / 0 评论 / 258 阅读 / 正在检测是否收录...
博客网址:www.shicoder.top
微信:kj11011029
欢迎加群聊天 :452380935

本篇文章主要实现了聊天服务器中一对一聊天和添加好友功能

一对一聊天

以下是聊天对应的json字符串格式

{"msgid":5,"id":2,"from":"lisi","to":1,"msg":"hello4"}

同样在ChatService中添加如下函数

  // 一对一聊天业务
  void oneChat(const TcpConnectionPtr &conn, json &js, Timestamp);
注意:锁的处理

在一对一聊天中,我们需要服务器知道哪些用户是在线的,哪些用户是离线的,对于在线用户,我们直接将聊天信息转发,对于离线好友,我们需要将信息先存储在离线信息表中,然后等对方在线后,再进行发送。所以我们采用一个map来保存useridconn的对应关系

  // 存储在线用户的连接,因为比如用户1向用户2发送请求,这时候需要服务器来做中间转化
  // 所以服务器需要知道用户2是否在线
  unordered_map<int, TcpConnectionPtr> _userConnMap;

这样在用户登陆的时候,若登陆成功,需要给当前用户的userid添加一个connmap键值对,同时利用锁进行处理,防止出现意外。

// 在这里加{}主要就是设置作用域,lock_guard<mutex> lock(_connMutex)中lock_guard会在构造函数加锁,
// 析构函数释放锁,同时由于只需要insert需要锁,所以加一个作用域,这样避免给不必要的地方加锁,浪费资源
{
    lock_guard<mutex> lock(_connMutex);
    // 记录用户连接信息
    _userConnMap.insert({id, conn});
}

//登陆成功,然后更新用户登陆信息
user.setState("online");
_usermodel.updateState(user);
json response;
response["msgid"] = LOGIN_MSG_ACK;
response["error"] = 0; //0表示成功
response["id"] = user.getId();
response["name"] = user.getName();

这样采用如下代码实现消息的转发或者消息的存储

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

    {
        lock_guard<mutex> lock(_connMutex);
        auto it = _userConnMap.find(toid);
        if (it != _userConnMap.end())
        {
            // toid在线,转发消息
            it->second->send(js.dump());
            return;
        }
    }

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

对于离线好友,我们需要在他登陆的时候,首先去查询_offlinemodel这个map中是否有自己对应的离线消息,若有,要先让服务器推送消息给用户,然后从map中删除对应的消息

注意,我在这里暂时打算让服务器用一个vector<string>存储离线消息
// 查询该用户是否有离线信息
vector<string> vec = _offlinemodel.query(id);
if (!vec.empty())
{
    response["offlinemsg"] = vec;
    // 再删除离线消息
    _offlinemodel.remove(id);
}

添加好友

以下是添加好友的json字符串

{"msgid":6,"id":1,"friendid":2}

同样在ChatService中添加如下代码

// 添加好友请求 msgid id friendid
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp)
{
    int userid = js["id"].get<int>();
    int friendid = js["friendid"].get<int>();

    // 存储好友信息
    _friendmodel.insert(userid, friendid);
}

首先需要在用户登陆的时候,服务器自动推送给用户其对应的好友,所以需要一个vector<User>来存储该用户对应的好友列表,同时将这个vectoruserid在数据库中保存,表结构如下

字段名称字段类型字段说明约束
useridINT用户idNOT NULL、联合主键
friendidINT好友idNOT NULL、联合主键

在用户登录成功之后,服务器会去查询用户的好友列表,然后推送给用户,其代码如下:

// 查询该用户的好友信息 并返回
vector<User> uservec = _friendmodel.query(id);
if (!uservec.empty())
{
    vector<string> vec2;
    for (auto &i : uservec)
    {
        json js;
        js["id"] = i.getId();
        js["name"] = i.getName();
        js["state"] = i.getState();
        vec2.emplace_back(js.dump());
    }
    response["friends"] = vec2;
}

其中的_friendmodel.query()使用的是多表联合查询,sql语句为

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

下一章我们实现下群组的创建与群组之间的聊天业务。

1

评论 (0)

取消