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.
 
 
 
 
 
 

174 lines
5.3 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::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;
}