1. 重构了WebSocket类的实现,并且设置其为项目的核心通信协议

This commit is contained in:
Misaki
2025-12-30 04:59:03 +08:00
parent 702b083e47
commit bb509e5409
10 changed files with 1225 additions and 280 deletions
+103
View File
@@ -0,0 +1,103 @@
//
// Created by misaki on 2025/12/29.
//
#pragma once
/**
* 本类为网络数据流会使用到的数据访问对象封装
* 目的是便于统一数据访问方式
* 同时屏蔽了端到端数据交换格式,使得上层调用不再需要关心数据格式,而只需要填入数据即可
*/
/**
* 简单描述一下Yosuga客户端所需要使用到的数据
* 主要为音频数据,控制信息,文本信息。
* 其中文本信息与音频数据为捆绑收发,并且其中还包括了一些特别的信息,例如音频时长等
* 控制信息与各种业务逻辑相关,例如模拟点击,模拟输入等
*/
#include <QObject>
#include <QString>
#include <QByteArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QScopedPointer>
#include <QMutex>
#include <functional>
/**
* 数据传输对象 (DTO) 定义
*/
// 音频文本捆绑数据结构
struct AudioDataPacket {
QString text; // 文本内容
QByteArray audioData; // 音频原始数据 (二进制)
int sampleRate; // 采样率
int channels; // 通道数
qint64 duration; // 时长 (ms)
AudioDataPacket() : sampleRate(16000), channels(1), duration(0) {}
};
// 控制指令数据结构 (预留)
enum class ControlType {
Click,
Input,
Scroll
};
struct ControlDataPacket {
ControlType action;
int x;
int y;
QString extraData;
};
/**
* NetworkDO
*/
class NetworkDO final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(NetworkDO) // 禁用拷贝
public:
// 单例访问点
static NetworkDO* getInstance();
// 显式销毁
static void destroy();
// 定义发送回调函数类型
using SenderFunc = std::function<void(const QString& type, const QJsonObject& data)>;
public:
// 注入发送接口
void registerSender(SenderFunc sender);
// 业务发送函数
void sendAudioPacket(const AudioDataPacket& packet);
void sendControlPacket(const ControlDataPacket& packet);
signals:
// 业务接收信号
void audioPacketReceived(const AudioDataPacket& packet);
void controlPacketReceived(const ControlDataPacket& packet);
void errorOccurred(const QString& errorMsg);
public slots:
// 接收底层 JSON 数据
void onDataReceived(const QString& type, const QJsonObject& data);
public:
~NetworkDO() override;
private:
// 构造/析构函数私有化
explicit NetworkDO(QObject *parent = nullptr);
// 内部处理逻辑
void handleAudioMessage(const QJsonObject& data);
static QScopedPointer<NetworkDO> m_instance;
static QMutex m_mutex;
SenderFunc m_sender; // 注入的发送器
};
+118
View File
@@ -0,0 +1,118 @@
//
// Created by misaki on 2025/12/29.
//
#include "NetWorkDO.h"
#include <QDebug>
#include <QMutexLocker>
// 初始化静态成员
QScopedPointer<NetworkDO> NetworkDO::m_instance;
QMutex NetworkDO::m_mutex;
// 单例实现 (QScopedPointer + Mutex)
NetworkDO* NetworkDO::getInstance()
{
if (m_instance.isNull()) {
QMutexLocker locker(&m_mutex);
if (m_instance.isNull()) {
// 使用 reset 创建实例,因为构造函数是私有的
m_instance.reset(new NetworkDO());
}
}
return m_instance.data();
}
void NetworkDO::destroy()
{
QMutexLocker locker(&m_mutex);
if (!m_instance.isNull()) {
m_instance.reset(); // 这会触发析构函数
}
}
NetworkDO::NetworkDO(QObject *parent) : QObject(parent)
{
qDebug() << "NetworkDO initialized";
}
NetworkDO::~NetworkDO()
{
qDebug() << "NetworkDO destroyed";
}
// 业务逻辑实现
void NetworkDO::registerSender(SenderFunc sender)
{
QMutexLocker locker(&m_mutex); // 简单保护一下赋值
m_sender = std::move(sender);
}
void NetworkDO::sendAudioPacket(const AudioDataPacket& packet)
{
// 检查发送器是否已注入
if (!m_sender) {
emit errorOccurred("Sender not registered! Call registerSender() first.");
return;
}
// 封装数据 (DTO -> JSON)
QJsonObject dataObj;
dataObj["text"] = packet.text;
// 音频转 Base64 字符串传输
dataObj["audio"] = QString::fromLatin1(packet.audioData.toBase64());
dataObj["sampleRate"] = packet.sampleRate;
dataObj["channels"] = packet.channels;
dataObj["duration"] = packet.duration;
// 调用底层发送 (解耦)
// "textAudio" 是与后端约定的协议类型
m_sender("textAudio", dataObj);
}
void NetworkDO::sendControlPacket(const ControlDataPacket& packet)
{
if (!m_sender) return;
QJsonObject dataObj;
dataObj["action"] = static_cast<int>(packet.action);
dataObj["x"] = packet.x;
dataObj["y"] = packet.y;
m_sender("control", dataObj);
}
void NetworkDO::onDataReceived(const QString& type, const QJsonObject& data)
{
// 根据类型分发
if (type == "textAudio") {
handleAudioMessage(data);
}
else if (type == "control") {
// handleControlMessage(data);
}
else {
qWarning() << "[NetworkDO] Received unknown type:" << type;
}
}
void NetworkDO::handleAudioMessage(const QJsonObject& data)
{
AudioDataPacket packet;
// 解析基础字段 (JSON -> DTO)
packet.text = data.value("text").toString();
packet.sampleRate = data.value("sampleRate").toInt(16000);
packet.channels = data.value("channels").toInt(1);
// 注意类型转换,确保 long long 精度
packet.duration = static_cast<qint64>(data.value("duration").toDouble());
// 解析音频 (Base64 -> Binary)
QString base64Audio = data.value("audio").toString();
if (!base64Audio.isEmpty()) {
packet.audioData = QByteArray::fromBase64(base64Audio.toLatin1());
}
// 通知上层业务
emit audioPacketReceived(packet);
}