【Qt】前后端交互---DataCenter类
设计目的
前后端交互系统中,创建并使用数据核心类的目的就是让该类作为客户端的数据中心,也就是说其负责管理客户端的所有数据与服务器的网络通信。
数据持久化
初始化数据文件
该函数设计的目的就是用于检查所需要的文件和目录是否存在,如果不存在创建,确保客户端和服务端的数据都是存储在同一位置上的。
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文件中读取数据,将其解析为可用的对象,然后填充到内存中的数据结构中。
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文件,从而确保数据在应用程序重启后依然可以使用。
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();
}
内存数据管理
用户信息管理
维护当前登录用户的信息,也就是用来保存当前登录用户的信息、好友列表等相关信息
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);
}
消息管理
负责管理最近的消息,聊天会话记录、未读信息等相关消息
// 在构造函数中初始化未读消息计数哈希表
DataCenter::DataCenter() : netClient(this)
{
unreadMessageCount = new QHash<QString, int>();
// 其他初始化操作
}
前后端通信
异步数据获取
通过netClient对象与服务器通信,使用异步方法来获取数据,避免阻塞线程,从而确保UI响应
void DataCenter::getMyselfAsync()
{
netClient.getMyself(loginSessionId);
}
数据同步
服务器接收到数据后,需要更新本地的数据
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,当数据到达后,会自动触发信号,但是不会造成线程阻塞
- 线程安全:信号和槽是可以跨线程工作的,确保不同线程下传递数据的安全性