【Qt6聊天室项目】 主界面功能实现

1. 获取当前用户的个人信息

1.1 前后端逻辑分析(主界面功能)

主界面上所有的前后端交互逻辑相同,分析到加载会话列表后其余功能仅实现。

核心逻辑总结

异步请求-响应模型

  • 客户端发起请求,向服务器发送包含会话ID的请求
  • 服务端处理请求并响应,根据的会话ID查找用户信息,并返回结果
  • 客户端处理响应,解析服务器的响应并更新UI显式界面

前后端交互逻辑概述

前端流程

  • MainWidget::initData:前端入口函数,主要就是负责初始化数据,与此同时客户端向DataCenter发送异步请求以获取个人信息
  • DataCenter::getMyselfAsync:向NetClient发送请求,NetClient负责与服务器进行通信
  • NetClient::getMyself:这个函数则是通过HTTP请求向服务器发送用户登录会话ID(loginSessionId)来获取个人信息

后端流程

  • 服务器的HttpServer收到来自NetClient的请求后,然后解析请求数据(也就是会话ID)
  • HttpServer::getUserInfo:服务器根据会话ID,查找用户信息并构建响应,然后将用户信息返回给客户端

客户端处理响应

  • 服务器返回的响应信息由NetClient接收,然后通过DataCenter,最后在MainWidget中展示用户的头像信息

代码逻辑实现分析(前后端交互逻辑)

前端发起请求

MainWidget中通过初始化信号槽函数中,使用connect函数建立了一个连接,当DataCenter::getMyselfDone信号发出后,会执行一个回调函数,这个回调函数的作用就是获取用户信息(myself)然以后设置头像

dataCenter->getMyselfAsync():异步请求,也就是调用该函数进一步通过网络底层获取用户信息。

connect(dataCenter, &DataCenter::getMyselfDone, this, [=]() {const auto* myself = dataCenter->getMyself();this->userAvatar->setIcon(myself->avatar);
});
dataCenter->getMyselfAsync();

 异步请求逻辑

在数据核心类中,调用getMyselfAsync(()函数,然后又会调用NetClient中的getMyselef函数,然后该函数会将登陆的sessionId传递给NetClient,最后由NetClient来处理网络通信

void DataCenter::getMyselfAsync() const {netClient.getMyself(loginSessionId);
}

 前后端通信的实现(NetClient与服务端)

  • 构造请求:使用proto构建请求体,该处使用的是GetUserInfoReq协议消息
  • 发送请求:sendHttpRequest方法,通过传递请求路劲和请求体,通过http客户端post请求,然后返回一个HTTP响应
  • 处理响应:在httpResp请求完成后,netClient会接收服务端返回的响应信息,通过httpleHttpResponse方法解析响应,解析成功后,将用户信息传递给DataCenter ,同时发出getMyselfDone信号,最后通知MainWidget更新UI
void NetClient::getMyself(const QString &loginSessionId) {// 1. 构造请求体bite_im::GetUserInfoReq req;req.setRequestId(makeRequestId());req.setSessionId(loginSessionId);// 2. 发送 HTTP 请求QByteArray body = req.serialize(&serializer);LOG() << "[获取个人信息] requestId=" << req.requestId() << ", sessionId=" << loginSessionId;QNetworkReply* httpResp = this->sendHttpRequest("/service/user/get_user_info", body);// 3. 处理 HTTP 响应connect(httpResp, &QNetworkReply::finished, this, [=]() {auto userInfoRsp = this->handleHttpResponse<bite_im::GetUserInfoRsp>(httpResp);if (!userInfoRsp) {return;}// b) 设置 DataCenter 中的用户信息dataCenter->resetMyself(userInfoRsp);// c) 发出信号通知获取完成emit dataCenter->getMyselfDone();});
}

代码实现逻辑(服务端处理请求)

  • 解析请求:服务端首先解析客户端传过来的请求体,然后根据sessionID查找用户信息
  • 构建响应:通过构建UserInfo对象,填充用户的详细信息,将这些信息装入响应体GetUserInfoRsp,设置success为true就表明操作成功
  • 返回响应:将序列化后的响应体返回给客户端,客户端收到这个响应后会解析数据,然后更新页面
QHttpServerResponse HttpServer::getUserInfo(const QHttpServerRequest &req) {// 解析请求bite_im::GetUserInfoReq pbReq;pbReq.deserialize(&serializer, req.body());LOG() << "[REQ 获取用户信息] requestId=" << pbReq.requestId() << ", sessionId=" << pbReq.sessionId();// 根据 sessionId 获取用户信息bite_im::UserInfo userInfo;userInfo.setUserId("1234");userInfo.setNickname("张三");userInfo.setDescription("这是个性签名");userInfo.setPhone("18612345678");userInfo.setAvatar(loadImageToByteArray(":/image/defaultAvatar.png"));// 构造响应bite_im::GetUserInfoRsp pbRsp;pbRsp.setSuccess(true);pbRsp.setUserInfo(userInfo);// 序列化响应体并发送QByteArray body = pbRsp.serialize(&serializer);return QHttpServerResponse(body, QHttpServerResponse::StatusCode::Ok);
}

代码实现逻辑(客户端处理响应) 

客户端接收到服务端返回的响应后,NetClient然后进行相应的处理

  • 解析响应:首先通过handleHttpResponse解析其中的HTTP响应体,生成GetUserInfoRsp对象
  • 更新数据:更新核心数据类中的数据
  • 发出信号:最后发出getMyselfDone信号,通知前端MainWidget数据获取完成,从而实现更新用户界面
auto userInfoRsp = this->handleHttpResponse<bite_im::GetUserInfoRsp>(httpResp);
if (!userInfoRsp) {return;
}
// b) 设置 DataCenter 中的数据
dataCenter->resetMyself(userInfoRsp);
// c) 发出信号通知获取完成
emit dataCenter->getMyselfDone();

不同文件交互梳理

类似于外卖系统中的订单处理

  • 客户端下单:用户提交外卖订单,这也就相当于MainWidget发起了获取用户信息的请求
  • 订单传递给平台:订单会进入外卖平台的处理系统,相对于DataCenter调用NetClient来发起网络请求
  • 平台传递订单给餐厅:外卖平台将订单信息发送给餐厅,类似于NetClient发送HTTP请求给服务器
  • 餐厅处理并回传订单状态:此时餐厅已经准备食品,然后将订单完成状态返回给平台,这也就是服务器返回用户信息给客户端
  • 平台通知客户端:外卖平台将订单完成状态通知用户,客户端根据订单的状态更新用户的UI界面,这也就对应这UI更新用户信息

架构实现分析 

该模块功能的实现使用MVC架构,也就是将数据、试图、控制逻辑三者进行分离,从而使得代码结构清晰模块化。具体来说就是Model负责数据管理、View负责UI展示、Controller负责业务逻辑的处理。

客户端

当网络请求发生的时候,打开主窗口,然后可以获取用户头像以及个人信息,然后头像显示到主窗口上,个人信息显示到个人资料上。 

构建HTTP请求

获取和重置用户信息(在核心数据类中实现)

请求处理封装成模版类型

 网络通信内部实现逻辑

获取个人信息逻辑 

  • 主窗口启动,关联信号槽,发起请求
  • 构造HTTP请求,然后处理响应
  • 响应处理完成后保存到中心数据类中,最后告知响应已经处理完成

测试服务器

分析HTTP服务器处理流程

该测试代码的任务就是负责处理“获取用户信息”请求,然后返回一个Protobuf序列化响应,使用QHttpServerRequest解析客户端请求,返回一个带有用户信息的QHttpServerResponse响应

  • 请求解析:从HTTP请求的body中提取数据,然后使用Protobuf反序列化成为GetUserInFoReq对象,提取请求中包含的用户信息
  • 构建响应:生成包含用户信息的GetUserInfoRsp响应性响应,并将其序列化后作为HTTP响应的body
  • 发送响应:使用protobuf序列化后的数据构建HTTP响应,设置响应头,并将其发送给客户端

服务器正常功能测试

 功能实现分析

根据前后端接口编写客户端(界面---dataCenter---netClient---服务器---反显示后---调用datacenter---给出一个信号---界面)---编写服务器---测试

2. 加载好友列表

实现目标与实现思路分析

  • 实现目标:从服务器上获取好友列表然后显示到客户端界面上
  • 实现方式
    • 从核心数据类中获取好友列表数据
    • 判定数据核心类中是否已经有数据
      • 如果有数据则直接加载本地数据
      • 如果没有数据则需要从服务器中获取数据
    • 更新数据内容到客户端上
      • 如果内存中有数据的话,则直接将数据从内存中拿取出显示即可
  • 总体逻辑
    • 内存读取:首先从本地缓存DataCenter中获取好友数据,如果缓存中有则直接加载到界面
    • 网络请求:缓存中没有数据则需要进行网络请求,向服务器获取好友列表
    • 数据存储:服务器返回数据后,先调用数据中心类的接口,清空原有数据列表中的数据,然后将新的好友列表加载到内存中缓存,方便后续的快速读取
    • 界面更新:网络请求完成后,通过信号的方式通知主界面,然后根据获取的新好友列表更新界面

       

本地文件加载逻辑实现

 

网络请求获取好友列表数据

向服务器发送好友列表请求

  • Protobuf构造GetFriendListenReq请求消息,设置RequestId 和 SessionId
  • 向服务器发起请求,并指定URL(预先约定好)
  •  connect()函数负责等待服务器的响应到来,然后接收数据
  • 响应到达后,解析响应,获取friendListResp,也就是好友列表响应 

 网络请求成功后将好友列表存储在核心数据类中

测试服务器逻辑

  • 注册路由
    • 监听指定的URL地址,当请求到达的时候,则调用getChatSessionList函数进行处理
  • 解析请求
    • 将客户端的请求体反序列化为缓冲区的Protobuf对象,同时通过日志记录
  • 构造响应
    • 初始化响应对象,然后设置请求ID、成功状态、错误信息
  • 发送响应
    • 构建所有会话信息后,服务器将响应序列化发送给客户端

前后端总体实现逻辑总结

 

 

3. 加载会话列表

逻辑分析

4. 加载好友申请列表

实现逻辑汇总 

 

 

 

 

5. 加载会话的最近消息

初始化逻辑 

该功能的实现并非在程序启动的时候,而是当用户点击某个会话的时候才会触发,下面从选择好友开始梳理其逻辑

 

 

逻辑梳理

 

服务器处理

 

客户端处理服务器响应

细节问题处理  

保证滚动条每次直接到达的末尾位置

6. 处理点击好友列表项 

功能分析

点击好友列表后

  • 切换到会话列表
  • 选中对应的会话,根据点击好友的userid和会话列表中的userid匹配
  • 消息展示区中,加载出对应会话的最近消息

逻辑整理

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ASP.NET Core 8.0 中使用 Hangfire 调度 API

在这篇博文中&#xff0c;我们将引导您完成将 Hangfire 集成到 ASP.NET Core NET Core 项目中以安排 API 每天运行的步骤。Hangfire 是一个功能强大的库&#xff0c;可简化 .NET 应用程序中的后台作业处理&#xff0c;使其成为调度任务的绝佳选择。继续阅读以了解如何设置 Hang…

计算机毕业设计PySpark+大模型高考推荐系统 高考分数线预测 高考爬虫 协同过滤推荐算法 Vue.js Django Hadoop 大数据毕设

基于Spark的高考报名信息推荐系统 系统用到的各项技术和工具的介绍&#xff1a; 1. Python Python是一种高级的、解释型的程序设计语言&#xff0c;因为其简洁而易学、可读性强等特点&#xff0c;在数据处理、人工智能、机器学习、Web开发等领域得到了广泛运用。在该系统中…

php AES 加解密(支持在线运行)

https://andi.cn/page/621792.html 这篇文章不仅给出了 php AES 加解密代码&#xff0c;而且可以在线运行来对数据进行加解密&#xff0c;满足实际中的一些需求。

C语言初阶七:C语言操作符详解(1)

#1024程序员节|征文# 这篇文章是对之前文章中操作符的补充&#xff0c;可以看之前的文章&#xff1a;C语言初阶&#xff1a;六.算数操作_如何用编程表示除法-CSDN博客 C语言操作符是用于执行各种运算和操作的符号。包括算术操作符&#xff08;如、-、*、/、%&#xff09;&#…

Python URL编码

在 Python 中&#xff0c;可以使用 urllib.parse模块对 URL 进行编码。 一、依赖安装 pip install urllib 二、URL编码 from urllib.parse import quoteurl rhttps://myshop.com/shop/shopList?query query {"id":14,"pageSize":10,"pageNum&quo…

[软件工程]—桥接(Brige)模式与伪码推导

桥接&#xff08;Brige&#xff09;模式与伪码推导 1.基本概念 1.1 动机 由于某些类型的固有的实现逻辑&#xff0c;使它们具有两个变化的维度&#xff0c;乃至多个维度的变化。如何应对这种“多维度的变化”&#xff1f;如何利用面向对象技术是的类型可以轻松的沿着两个乃至…

新鲜出炉面试题之【说说spring spring MVC spring boot的区别】

Spring MVC 和 Spring Boot 是 Spring 框架的一部分&#xff0c;但它们的目的和用途有所不同。下面详细阐述这两者之间的区别。 1. 概念 Spring MVC&#xff1a; Spring MVC 是一种基于请求-响应模式的 Web 框架&#xff0c;属于 Spring 框架的一部分。它提供了一种分离的方式…

html 轮播图效果

轮播效果&#xff1a; 1、鼠标没有移入到banner,自动轮播 2、鼠标移入&#xff1a;取消自动轮播、移除开始自动轮播 3、点击指示点开始轮播到对应位置 4、点击前一个后一个按钮&#xff0c;轮播到上一个下一个图片 注意 最后一个图片无缝滚动&#xff0c;就是先克隆第一个图片…

【树莓派系统安装】Raspberry Pi OS操作系统烧录与VNC远程树莓派实战

文章目录 前言1. 使用 Raspberry Pi Imager 安装 Raspberry Pi OS2. Windows安装VNC远程树莓派3. 使用VNC Viewer公网远程访问树莓派3.1 安装Cpolar步骤3.2 配置固定的公网地址3.3 VNC远程连接测试 4. 固定远程连接公网地址4.1 固定TCP地址测试 前言 本文主要介绍如何在树莓派…

Linux使用Dockerfile部署Tomcat以及jdk

资源准备 首先提供本教程所有资源包。 当然也可以根据自己需求去官网下载。 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;f31y #我们开始吧 首先我们需要一台linux操作系统的机器&#xff0c;当然windows也是可以的&#xff0c;本系列教程是基于Linux的&#…

利用数据库过滤和元数据提取提升多跳查询的RAG性能

人工智能咨询培训老师叶梓 转载标明出处 大模型在处理需要从多个文档中检索和推理信息的多跳查询时&#xff0c;常常表现不佳&#xff0c;因为它们需要从多个来源检索和推理证据。图1展示了一个简单的RAG实现用于MultiHop-RAG查询。图中显示了用户查询、嵌入向量数据库、提示&…

解决:git SSL certificate problem: unable to get local issuer certificate

在使用Git进行代码交流和版本控制过程中&#xff0c;可能会遇到SSL证书问题。这通常是由于Git客户端无法验证SSL证书的合法性而引起的。当我们尝试与Git服务器建立安全连接时&#xff0c;Git客户端将会验证服务器端提供的SSL证书是否由受信任的证书颁发机构&#xff08;Certifi…

三数之和(15)

打回现实的一道题 思路&#xff1a;先将数组进行排序 遍历数组&#xff0c;使用left标记i1,right标记nums.length-1 如果三数之和(nums[i]nums[left]nums[right])大于0&#xff0c;right--,如果小于0&#xff0c;left 注意&#xff1a;1、使用set集合进行去重 2、找到…

链路分析对性能测试的意义

目录 一、白盒能力的提升 二、人员技术门槛的提升 链路分析的出现对测试工程师也带来了不同的影响&#xff0c;能实际提升测试工程师的分析能力&#xff0c;但是需要测试工程师具备主动的自我提升意识。 一、白盒能力的提升 传统的性能测试主要以TPS、响应时间、成功率等用户…

【工具】Ghidra|Ghidra 安装过程以及脚本运行方式

文章目录 前言安装 java下载 Ghidra打开 Ghidra 使用 Ghidra步骤 1&#xff1a;打开 Ghidra 并加载项目步骤 2&#xff1a;打开 Script Manager步骤 3&#xff1a;新建脚本并编写代码步骤 4&#xff1a;保存脚本步骤 5&#xff1a;运行脚本注意事项 前言 我的用途&#xff1a;…

【Prometheus】为Prometheus设置basic_auth访问权限

Prometheus目前已经成为国、内外互联网行业&#xff0c;一款非常知名的免费监控工具&#xff0c;我们可以通过它&#xff0c;以及Prometheus官方、第三方提供的一些exporter工具&#xff0c;对系统、中间件、数据库等一系列的软、硬件的运行数据&#xff0c;进行采集、存储、监…

【在Win11下安装ubuntu +图形化界面】

在win11下安装ubuntu 一、安装流程1. 前期准备&#xff1a;先配置好基础设置2. 安装 ubuntu3. ubuntu进行配置4. 下载图形化界面 并安装 二、遇到的问题问题1. win11安装wsl报错&#xff1a;无法解析服务器的名称或地址1. 方法一&#xff1a;更改DNS&#xff08;对本人无效&…

Java8中Stream、Function、Opotions特性使用案例

所有数据都基于UserInfo类&#xff0c;其中包含了 userId、userName、course、score 等字段&#xff0c;下面是如何使用Options、 Stream 、Function来处理 UserInfo 对象列表的一些示例 List<UserInfo> userInfoList Arrays.asList(new UserInfo(1L, "Alice"…

闯关leetcode——206. Reverse Linked List

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/reverse-linked-list/ 内容 Given the head of a singly linked list, reverse the list, and return the reversed list. Example 1: Input: head [1,2,3,4,5] Output: [5,4,3,2,1] Example 2:…

【23CSPJ普及组】一元二次方程(uqe)

时间限制: 1000 ms 内存限制: 524288 KB 【题目描述】 众所周知&#xff0c;对一元二次方程 &#x1d44e;&#x1d44f;&#x1d465;&#x1d450;0,(&#x1d44e;≠0)&#xff0c;可以用以下方式求实数解&#xff1a; ∙∙ 计算 Δ−4ac&#xff0c;则: 1. 若 Δ&…