简介
本文介绍如何实现用户查找和好友申请功能。查找和申请好友会涉及前后端通信和rpc服务间调用。所以目前先从客户端入手,搜索用户后发送查找好友申请请求给服务器,服务器收到后判断是否存在,如果不存在则显示未找到,如果存在则显示查找到的结果
点击查询
客户端点击搜索列表的添加好友item后,先弹出一个模态对话框,上面有loading动作表示加载,直到服务器返回结果
1 | void SearchList::slot_item_clicked(QListWidgetItem *item) |
_send_pending为新增的成员变量,如果为true则表示发送阻塞.构造函数中将其设置为false。
waitPending函数为根据pending状态展示加载框
1 | void SearchList::waitPending(bool pending) |
当我们发送数据后服务器会处理,返回ID_SEARCH_USER_RSP包,所以客户端要实现对ID_SEARCH_USER_RSP包的处理
1 | _handlers.insert(ID_SEARCH_USER_RSP, [this](ReqId id, int len, QByteArray data){ |
将搜索到的结果封装为search_info发送给SearchList类做展示, search_list中连接信号和槽
1 | //连接搜索条目 |
slot_user_search槽函数弹出搜索结果
1 | void SearchList::slot_user_search(std::shared_ptr<SearchInfo> si) |
FindSuccessDlg是找到的结果展示,FindFailDlg是未找到结果展示。以下为FindSuccessDlg的ui布局
具体声明如下
1 | class FindSuccessDlg : public QDialog |
具体实现如下
1 | FindSuccessDlg::FindSuccessDlg(QWidget *parent) : |
类似的FindFailDlg也是这种思路,大家自己实现即可。
服务器查询逻辑
chatserver服务器要根据客户端发送过来的用户id进行查找,chatserver服务器需先注册ID_SEARCH_USER_REQ和回调函数
1 | void LogicSystem::RegisterCallBacks() { |
SearchInfo根据用户uid查询具体信息
1 | void LogicSystem::SearchInfo(std::shared_ptr<CSession> session, const short& msg_id, const string& msg_data) { |
到此客户端和服务器搜索查询的联调功能已经解决了。
客户端添加好友
当Client1搜索到好友后,点击添加弹出信息界面,然后点击确定即可向对方Client2申请添加好友,这个请求要先发送到Client1所在的服务器Server1,服务器收到后判断Client2所在服务器,如果Client2在Server1则直接在Server1中查找Client2的连接信息,没找到说明Client2未在内存中,找到了则通过Session发送tcp给对方。如果Client2不在Server1而在Server2上,则需要让Server1通过grpc接口通知Server2,Server2收到后继续判断Client2是否在线,如果在线则通知。
如下图,Client1想和Client2以及Client3分别通信,需要先将请求发给Client1所在的Server1,再考虑是否rpc调用。
客户端在ApplySure槽函数中添加好友请求
1 | void ApplyFriend::SlotApplySure() |
另一个客户端会收到服务器通知添加好友的请求,所以在TcpMgr里监听这个请求
1 | _handlers.insert(ID_NOTIFY_ADD_FRIEND_REQ, [this](ReqId id, int len, QByteArray data) { |
服务调用
服务器要处理客户端发过来的添加好友的请求,并决定是否调用rpc通知其他服务。
先将AddFriendApply函数注册到回调map里
1 | void LogicSystem::RegisterCallBacks() { |
接下来实现AddFriendApply
1 | void LogicSystem::AddFriendApply(std::shared_ptr<CSession> session, const short& msg_id, const string& msg_data) { |
上面的函数中先更新数据库将申请写入数据库中
1 | bool MysqlMgr::AddFriendApply(const int& from, const int& to) { |
内部调用dao层面的添加好友请求
1 | bool MysqlDao::AddFriendApply(const int& from, const int& to) { |
添加完成后判断要通知的对端是否在本服务器,如果在本服务器则直接通过uid查找session,判断用户是否在线,如果在线则直接通知对端。
如果不在本服务器,则需要通过rpc通知对端服务器。rpc的客户端这么写即可。
1 | AddFriendRsp ChatGrpcClient::NotifyAddFriend(std::string server_ip, const AddFriendReq& req) { |
同样rpc的服务端也要实现,我们先将rpc客户端和服务端的逻辑都在ChatServer1写好,然后复制给ChatServer2即可。 rpc的服务实现如下
1 | Status ChatServiceImpl::NotifyAddFriend(ServerContext* context, const AddFriendReq* request, |
上面的代码也是判断要通知的客户端是否在内存中,如果在就通过session发送tcp请求。
将ChatServer1的代码拷贝给ChatServer2,重启两个服务,再启动两个客户端,一个客户端申请另一个客户端,通过查看客户端日志是能看到申请信息的。
申请显示
接下来被通知申请的客户端要做界面显示,我们实现被通知的客户端收到sig_friend_apply信号的处理逻辑。在ChatDialog的构造函数中连接信号和槽
1 | //连接申请添加好友信号 |
实现申请好友的槽函数
1 | void ChatDialog::slot_apply_friend(std::shared_ptr<AddFriendApply> apply) |
这样就能显示新的申请消息和红点了。具体添加一个新的申请条目到申请好友页面的逻辑如下:
1 | void ApplyFriendPage::AddNewApply(std::shared_ptr<AddFriendApply> apply) |
测试效果, 收到对方请求后如下图
登录加载申请
当用户登录后,服务器需要将申请列表同步给客户端, 写在登录逻辑里。
1 | //从数据库获取申请列表 |
获取好友申请信息函数
1 | bool LogicSystem::GetFriendApplyInfo(int to_uid, std::vector<std::shared_ptr<ApplyInfo>> &list) { |
dao层面实现获取申请列表
1 | bool MysqlMgr::GetApplyList(int touid, |
好友认证界面
客户端需要实现好友认证界面,当点击同意对方好友申请后,弹出认证信息,点击确定后将认证同意的请求发给服务器,服务器再通知申请方,告知对方被申请人已经同意加好友了。认证界面和申请界面类似, 这个大家自己实现即可。
认证界面的函数和逻辑可以照抄申请好友的逻辑。
1 | AuthenFriend::AuthenFriend(QWidget *parent) : |
源码连接
https://gitee.com/secondtonone1/llfcchat