175 lines
5.3 KiB
C++
175 lines
5.3 KiB
C++
/*
|
|
* 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::sendTo(quint64 dstPeerId, const QByteArray& data) {
|
|
QByteArray msg = buildMessage(kDataSend, dstPeerId, 0, data);
|
|
m_socket->write(msg);
|
|
}
|
|
|
|
void MsgClient::requestPublicKey() {
|
|
QByteArray msg = buildMessage(kCtlGetPubkey, 0, 0, QByteArray());
|
|
m_socket->write(msg);
|
|
}
|
|
|
|
void MsgClient::requestPrivateKey() {
|
|
QByteArray msg = buildMessage(kCtlGetPrivkey, 0, 0, QByteArray());
|
|
m_socket->write(msg);
|
|
}
|
|
|
|
void MsgClient::requestPeerInfo(quint64 peerId) {
|
|
QByteArray msg = buildMessage(kCtlGetPeerInfo, peerId, 0, QByteArray());
|
|
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 kRspPubkey:
|
|
emit publicKeyReceived(payload);
|
|
break;
|
|
case kRspPrivkey:
|
|
emit privateKeyReceived(payload);
|
|
break;
|
|
case kRspPeerInfo:
|
|
if (payload.size() >= 64) {
|
|
emit peerInfoReceived(id, payload.left(32), payload.mid(32, 32));
|
|
}
|
|
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;
|
|
}
|