/* * msg_client.cpp — C++ транспортный клиент */ #include "msg_client.h" #include MsgClient::MsgClient(QObject* parent) : QObject(parent), m_socket(new QTcpSocket(this)) { QObject::connect(m_socket, &QTcpSocket::connected, this, &MsgClient::connected); QObject::connect(m_socket, &QTcpSocket::disconnected, this, [this]() { if (m_boundPeerId) { m_boundPeerId = 0; emit unbound(); } emit disconnected(); }); QObject::connect(m_socket, &QTcpSocket::readyRead, this, &MsgClient::onReadyRead); QObject::connect(m_socket, &QAbstractSocket::errorOccurred, this, &MsgClient::onSocketError); } MsgClient::~MsgClient() { disconnectFromHost(); } void MsgClient::connectToHost(const QString& host, quint16 port) { m_recvBuffer.clear(); m_boundPeerId = 0; m_socket->connectToHost(host, port); } void MsgClient::disconnectFromHost() { if (m_socket->state() != QAbstractSocket::UnconnectedState) m_socket->disconnectFromHost(); m_recvBuffer.clear(); if (m_boundPeerId) { m_boundPeerId = 0; emit unbound(); } } bool MsgClient::isConnected() const { return m_socket->state() == QAbstractSocket::ConnectedState; } void MsgClient::bind(quint64 peerId) { QByteArray msg = buildMessage(kCtlBind, peerId, 0, QByteArray()); m_socket->write(msg); } void MsgClient::unbind() { if (!m_boundPeerId) return; QByteArray msg = buildMessage(kCtlUnbind, 0, 0, QByteArray()); m_socket->write(msg); m_boundPeerId = 0; emit unbound(); } void MsgClient::send(const QByteArray& data) { if (!m_boundPeerId) return; QByteArray msg = buildMessage(kDataSend, m_boundPeerId, 0, data); m_socket->write(msg); } void MsgClient::send(const uint8_t* data, size_t len) { send(QByteArray(reinterpret_cast(data), static_cast(len))); } void MsgClient::sendSigned(const QByteArray& data) { if (!m_boundPeerId) return; QByteArray msg = buildMessage(kDataSend, m_boundPeerId, kOptSigned, data); m_socket->write(msg); } /* ============================================================================ * Внутренние * ============================================================================ */ void MsgClient::onReadyRead() { m_recvBuffer.append(m_socket->readAll()); if (m_recvBuffer.size() > static_cast(kMaxMsgSize)) { m_recvBuffer.clear(); emit errorOccurred(0xFF, "recv buffer overflow"); return; } processMessages(); } void MsgClient::onSocketError(QAbstractSocket::SocketError) { emit errorOccurred(0xFF, m_socket->errorString()); } void MsgClient::processMessages() { while (static_cast(m_recvBuffer.size()) >= kHdrSize) { uint16_t totalSize; std::memcpy(&totalSize, m_recvBuffer.constData(), 2); if (totalSize < kHdrSize || totalSize > kMaxMsgSize) { m_recvBuffer.clear(); emit errorOccurred(0xFF, "invalid message size"); return; } if (m_recvBuffer.size() < totalSize) break; uint8_t type = static_cast(m_recvBuffer[2]); uint64_t id; std::memcpy(&id, m_recvBuffer.constData() + 3, 8); QByteArray payload = m_recvBuffer.mid(static_cast(kHdrSize), totalSize - static_cast(kHdrSize)); dispatch(type, id, payload); m_recvBuffer.remove(0, totalSize); } } void MsgClient::dispatch(uint8_t type, quint64 id, const QByteArray& payload) { switch (type) { case kRspBindOk: m_boundPeerId = id; emit bound(id); break; case kDataRecv: emit received(id, payload); break; case kRspError: uint8_t code = payload.size() > 0 ? static_cast(payload[0]) : 0; QString msg = QString::fromUtf8(payload.mid(1)); emit errorOccurred(code, msg); break; } } QByteArray MsgClient::buildMessage(uint8_t type, quint64 id, uint8_t options, const QByteArray& data) { uint16_t totalSize = static_cast(kHdrSize + data.size()); QByteArray msg; msg.resize(totalSize); std::memcpy(msg.data(), &totalSize, 2); msg[2] = static_cast(type); std::memcpy(msg.data() + 3, &id, 8); msg[11] = static_cast(options); if (!data.isEmpty()) std::memcpy(msg.data() + kHdrSize, data.constData(), data.size()); return msg; }