You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
4.4 KiB
143 lines
4.4 KiB
/* |
|
* msg_client.cpp — C++ транспортный клиент |
|
*/ |
|
|
|
#include "msg_client.h" |
|
#include <cstring> |
|
|
|
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<const char*>(data), static_cast<int>(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<int>(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<size_t>(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<uint8_t>(m_recvBuffer[2]); |
|
uint64_t id; |
|
std::memcpy(&id, m_recvBuffer.constData() + 3, 8); |
|
QByteArray payload = m_recvBuffer.mid(static_cast<int>(kHdrSize), |
|
totalSize - static_cast<uint16_t>(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<uint8_t>(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<uint16_t>(kHdrSize + data.size()); |
|
QByteArray msg; |
|
msg.resize(totalSize); |
|
std::memcpy(msg.data(), &totalSize, 2); |
|
msg[2] = static_cast<char>(type); |
|
std::memcpy(msg.data() + 3, &id, 8); |
|
msg[11] = static_cast<char>(options); |
|
if (!data.isEmpty()) |
|
std::memcpy(msg.data() + kHdrSize, data.constData(), data.size()); |
|
return msg; |
|
}
|
|
|