【Qt】前后端交互---DataCenter类

设计目的

前后端交互系统中,创建并使用数据核心类的目的就是让该类作为客户端的数据中心,也就是说其负责管理客户端的所有数据与服务器的网络通信。

数据持久化

初始化数据文件

该函数设计的目的就是用于检查所需要的文件和目录是否存在,如果不存在创建,确保客户端和服务端的数据都是存储在同一位置上的。

cpp 复制代码
void DataCenter::initDataFile()
{
    QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QString filePath = basePath + "/ChatClient.json";

    QDir dir;
    if (!dir.exists(basePath)) {
        dir.mkpath(basePath);
    }

    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        LOG() << "打开文件失败!" << file.errorString();
        return;
    }
    QString data = "{\n\n}";
    file.write(data.toUtf8());
    file.close();
}

加载数据文件

也就是从本地JSON文件中读取数据,将其解析为可用的对象,然后填充到内存中的数据结构中。

cpp 复制代码
void DataCenter::loadDataFile()
{
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/ChatClient.json";
    QFileInfo fileInfo(filePath);
    if (!fileInfo.exists()) {
        initDataFile();
    }

    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        LOG() << "打开文件失败!" << file.errorString();
        return;
    }

    QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll());
    if (jsonDoc.isNull()) {
        LOG() << "解析JSON文件失败! JSON文件格式错误";
        file.close();
        return;
    }

    QJsonObject jsonObj = jsonDoc.object();
    this->loginSessionId = jsonObj["loginSessionId"].toString();

    this->unreadMessageCount->clear();
    QJsonObject jsonUnread = jsonObj["unread"].toObject();
    for (auto it = jsonUnread.begin(); it != jsonUnread.end(); ++it) {
        this->unreadMessageCount->insert(it.key(), it.value().toInt());
    }
    file.close();
}

保存数据文件

设计该方法的目的就是将当前的数据状态写回到JSON文件,从而确保数据在应用程序重启后依然可以使用。

cpp 复制代码
void DataCenter::saveDataFile()
{
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/ChatClient.json";

    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        LOG() << "文件打开失败" << file.errorString();
        return;
    }

    QJsonObject jsonObj;
    jsonObj["loginSessionId"] = loginSessionId;

    QJsonObject jsonUnread;
    for (auto it = unreadMessageCount->begin(); it != unreadMessageCount->end(); ++it) {
        jsonUnread[it.key()] = it.value();
    }
    jsonObj["unread"] = jsonUnread;

    QJsonDocument jsonDoc(jsonObj);
    QString s = jsonDoc.toJson();
    file.write(s.toUtf8());

    file.close();
}

内存数据管理

用户信息管理

维护当前登录用户的信息,也就是用来保存当前登录用户的信息、好友列表等相关信息

cpp 复制代码
UserInfo *DataCenter::getMyself()
{
    return myself;
}

void DataCenter::resetMyself(std::shared_ptr<bite_im::GetUserInfoRsp> resp)
{
    if (myself == nullptr) {
        myself = new UserInfo();
    }
    const bite_im::UserInfo &userInfo = resp->userInfo();
    myself->load(userInfo);
}

消息管理

负责管理最近的消息,聊天会话记录、未读信息等相关消息

cpp 复制代码
// 在构造函数中初始化未读消息计数哈希表
DataCenter::DataCenter() : netClient(this)
{
    unreadMessageCount = new QHash<QString, int>();
    // 其他初始化操作
}

前后端通信

异步数据获取

通过netClient对象与服务器通信,使用异步方法来获取数据,避免阻塞线程,从而确保UI响应

cpp 复制代码
void DataCenter::getMyselfAsync()
{
    netClient.getMyself(loginSessionId);
}

数据同步

服务器接收到数据后,需要更新本地的数据

cpp 复制代码
void DataCenter::resetMyself(std::shared_ptr<bite_im::GetUserInfoRsp> resp)
{
    if (myself == nullptr) {
        myself = new UserInfo();
    }
    const bite_im::UserInfo &userInfo = resp->userInfo();
    myself->load(userInfo);
}

数据核心类与其他类的联系

前后端交互数据存储在DataCenter

数据核心类的作用就是在客户端应用程序 中集中管理和存储与前后端交互的相关数据。其是作为数据核心类存在的。

  • 统一存储应用中所需要的数据,例如用户信息、好友列表、消息记录等,都集中存储在一个地方,方便访问和管理
  • 通过集中管理保证数据一致性,避免不同模块之间的数据不一致的情况
  • 数据共享,通过将需要使用的数据都存放在一个类中,从而供其他的对象对其数据进行调用

DataCenter和MainWindow类的关系

首先MainWindow就是应用程序的主界面,DataCenter是数据管理类,专门负责处理数据的存取与服务器通信。所以两者之间主要存在两种关系。

  • **MainWindow是依赖于DataCenter。**因为主窗口需要从数据核心类中获取数据显示,就必须显示个人信息,就需要通过数据核心类来获取
  • **DataCenter独立于MainWindow。**数据核心类是不与界面直接进行交互的,而是专门负责数据处理和存储。

其次从实例对应关系上分析,也就是从数据核心类实例后对象与其他部分的关系分析

  • **DataCenter类设计成单例模式,**保证整个应用程序中只有一个DataCenter实例,这也是为了确保数据的一致性和共享性
  • 用户级别考虑,因为一个数据核心类中肯定是只可以存储一个用户的数据和信息,也就是说每个用户都会有自己的数据核心类和主窗口实例
  • 用户切换情况下,如果支持多用户的客户端应用程序,那么在多用户切换的情况下,需要清空核心数据类中存储的信息,然后重新加载新的数据

前后端交互过程中Qt应用层和原理层

应用层面

  • **异步通信机制:**客户端发送请求后到服务器后,通常是不会阻塞等待服务器响应的,而是通过异步机制等待结果,所以应用层使用的是异步机制
  • **信号与槽机制:**当服务器处理完成请求后并返回结果后,客户端的网络模块会发送一个信号,通知应用程序已经准备好
  • **处理响应:**应用程序中预先连接了对应的槽函数,也就是在信号发出后,槽函数就会被调用,进行数据处理和更新页面

原理层面

  • **事件驱动:**服务器的运行逻辑是依靠事件驱动模型实现的,也就是当有事件的时候,事件循环会触发相应的处理函数
  • **异步I/O:**Qt的网络模块内部使用的是异步IO,当数据到达后,会自动触发信号,但是不会造成线程阻塞
  • **线程安全:**信号和槽是可以跨线程工作的,确保不同线程下传递数据的安全性
相关推荐
wangjing_052211 分钟前
C语言练习.if.else语句.strstr
c语言·开发语言
Tony_long748315 分钟前
Python学习——字符串操作方法
开发语言·c#
SoraLuna44 分钟前
「Mac玩转仓颉内测版26」基础篇6 - 字符类型详解
开发语言·算法·macos·cangjie
出逃日志1 小时前
JS的DOM操作和事件监听综合练习 (具备三种功能的轮播图案例)
开发语言·前端·javascript
前端青山2 小时前
React事件处理机制详解
开发语言·前端·javascript·react.js
black0moonlight3 小时前
ISAAC Gym 7. 使用箭头进行数据可视化
开发语言·python
时光の尘3 小时前
C语言菜鸟入门·关键字·int的用法
c语言·开发语言·数据结构·c++·单片机·链表·c
坊钰3 小时前
【Java 数据结构】时间和空间复杂度
java·开发语言·数据结构·学习·算法
Edward-tan3 小时前
c语言数据结构与算法--简单实现线性表(顺序表+链表)的插入与删除
c语言·开发语言·链表
武昌库里写JAVA3 小时前
一文读懂Redis6的--bigkeys选项源码以及redis-bigkey-online项目介绍
c语言·开发语言·数据结构·算法·二维数组