/*
* NetworkMessage.cpp
*
* Created on: 18.01.2013
* Author: sr
*/
#include <QtCore>
#include <QtNetwork>
#include "networkmessage.h"
#define HEADER_LEN 8
#define MAX_MSG_LEN 60000
#define BYTE_SWAP4(x) \
((((x) & 0xFF000000u) >> 24) | \
(((x) & 0x00FF0000u) >> 8) | \
(((x) & 0x0000FF00u) << 8) | \
(((x) & 0x000000FFu) << 24))
#define BYTE_SWAP2(x) \
((((x) & 0xFF00u) >> 8) | \
(((x) & 0x00FFu) << 8))
static quint16 _htons(const quint16 x)
{
if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
return x;
return quint16(BYTE_SWAP2(x));
}
static quint32 _htonl(const quint32 x)
{
if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
return x;
return quint32(BYTE_SWAP4(x));
}
static quint16 _ntohs(const char *in)
{
return quint16(quint8(in[0]) << 8
| quint8(in[1]));
}
static quint32 _ntohl(const char *in)
{
return quint32(quint8(in[0])) << 24
| quint32(quint8(in[1])) << 16
| quint32(quint8(in[2])) << 8
| quint32(quint8(in[3]));
}
/*
// ####################################################### \\ ° . °
\\ ####################################################### // \___/
*/
NetworkMessage::NetworkMessage() :
_buffer(nullptr), _bufferSize(0), _bufferPos(0), _lastBufferSize(0), _mode(0)
{
//
}
NetworkMessage::~NetworkMessage()
{
if (_buffer)
delete[] _buffer;
}
inline void NetworkMessage::allocBuffer()
{
if (_lastBufferSize < _bufferSize || _buffer == nullptr) {
if (_buffer)
delete[] _buffer;
_lastBufferSize = _bufferSize;
_buffer = new char[_lastBufferSize];
}
}
int NetworkMessage::readMessage(QAbstractSocket* socket)
{
// Check/Set the _mode variable, so read and write calls are not mixed
if (_mode != 1) {
if (_mode != 0) {
qDebug("NetworkMessage::readMessage(TCP) called when class was in mode %d!", _mode);
return NM_READ_FAILED;
}
_mode = 1;
}
// buffer size == 0 means the header hasn't been received yet. do so and set things up
if (_bufferSize == 0) {
if (socket->bytesAvailable() < HEADER_LEN)
return NM_READ_INCOMPLETE;
char header[HEADER_LEN];
if (socket->read(header, HEADER_LEN) != HEADER_LEN) {
qDebug("FIXME: Socket said 8 bytes available, but could not read 8...");
return NM_READ_FAILED;
}
if (!this->parseHeader(header))
return NM_READ_FAILED;
//qDebug() << "Expecting message of " << _bufferSize << " bytes";
allocBuffer();
}
if (_bufferSize > _bufferPos) {
while (_bufferSize > _bufferPos && socket->bytesAvailable() > 0) {
const qint64 ret = socket->read(_buffer + _bufferPos, _bufferSize - _bufferPos);
//qDebug() << "Read " << ret << " bytes";
if (ret < 0 || ret > (_bufferSize - _bufferPos)) {
qDebug("Socket read failed (TCP), return code %d", int(ret));
return NM_READ_FAILED;
}
_bufferPos += quint32(ret);
//qDebug() << "Buffer has now " << _bufferPos << " of " << _bufferSize << " bytes";
}
if (_bufferSize == _bufferPos) {
if (!this->parseMessage(_buffer))
return NM_READ_FAILED;
}
}
return NM_READ_OK;
}
int NetworkMessage::readMessage(char* data, quint32 len)
{
// Check/Set the _mode variable, so read and write calls are not mixed
if (_mode != 1) {
if (_mode != 0) {
qDebug("NetworkMessage::readMessage(UDP) called when class was in mode %d!", _mode);
return NM_READ_FAILED;
}
_mode = 1;
}
if (len < HEADER_LEN) {
qDebug("UDP message shorter than 8 bytes. ignored.");
return NM_READ_FAILED;
}
if (!this->parseHeader(data))
return NM_READ_FAILED;
if (len != _bufferSize + HEADER_LEN) {
qDebug("UDP packet has wrong size. Is %d, expected %d", int(_bufferSize), len - HEADER_LEN);
return NM_READ_FAILED;
}
return this->parseMessage(data + HEADER_LEN) ? NM_READ_OK : NM_READ_FAILED;
}
bool NetworkMessage::parseHeader(char *header)
{
if (header[0] != 'P' || header[1] != 'V' || header[2] != 'S' || header[3] != '2') {
qDebug("Protocol magic wrong.");
return false;
}
_bufferPos = 0;
_bufferSize = _ntohl(header + 4);
if (_bufferSize > MAX_MSG_LEN) {
qDebug("Disconnecting Client: MAX_MSG_LEN exceeded.");
return false;
}
if (_bufferSize < 4) { // TODO: magic number. msg needs to be at least 4 bytes for 1 key/value pair of length 0 each.
qDebug("A Client sent an empty message.");
return false;
}
return true;
}
bool NetworkMessage::parseMessage(char *buffer)
{
char *ptr = buffer;
while (_bufferSize - (ptr - buffer) >= 4) {
const quint16 keyLen = _ntohs(ptr);
ptr += 2;
const quint16 valLen = _ntohs(ptr);
ptr += 2;
if (_bufferSize - (ptr - buffer) < keyLen + valLen) {
qDebug() << "Warning: Error parsing message. key(" << keyLen << ")+value(" << valLen
<< ") length > total remaining bytes (" << (_bufferSize - (ptr - buffer)) << ")";
return false;
}
_fields.insert(QByteArray(ptr, keyLen), QByteArray(ptr + keyLen, valLen));
//qDebug() << "Got " << QString::fromUtf8(ptr, keyLen) << " -> " << QString::fromUtf8(ptr + keyLen, valLen);
ptr += keyLen + valLen;
}
_mode = 3;
return true;
}
bool NetworkMessage::writeMessage(QAbstractSocket * const socket)
{
if (_mode != 2) {
if (_mode == 1) {
qDebug("NetworkMessage::writeMessage called when class was in mode %d!", _mode);
return false;
}
_mode = 1;
_bufferPos = 0;
}
// key/value pairs have not been serialized yet...
if (_bufferSize == 0) {
this->serializeMessage();
}
const qint64 ret = socket->write(_buffer + _bufferPos, _bufferSize - _bufferPos);
if (ret == 0)
return true;
if (ret < 0 || ret > (_bufferSize - _bufferPos))
return false;
_bufferPos += quint32(ret);
if (_bufferPos == _bufferSize) {
_bufferPos = 0;
_mode = 4;
}
return true;
}
bool NetworkMessage::writeMessage(QUdpSocket* socket, const QHostAddress& address, quint16 port)
{
if (_mode != 4) {
if (_mode == 1) {
qDebug("NetworkMessage::writeMessage called when class was in mode %d!", _mode);
return false;
}
_mode = 4;
_bufferPos = 0;
}
// key/value pairs have not been serialized yet...
if (_bufferSize == 0) {
this->serializeMessage();
}
const qint64 ret = socket->writeDatagram(_buffer, _bufferSize, address, port);
if (ret != _bufferSize)
return false;
if (ret < 0)
return false;
_bufferPos = 0;
return true;
}
void NetworkMessage::serializeMessage()
{
QByteArray buf;
//qDebug() << "Default size: " << buf.capacity();
buf.reserve(_lastBufferSize > 0 ? int(_lastBufferSize) : 200);
for (QHash<QByteArray, QByteArray>::const_iterator it = _fields.begin(); it != _fields.end(); ++it) {
const QByteArray &ba = it.key();
const QByteArray &val = it.value();
quint16 keyLen = _htons(quint16(ba.size()));
quint16 valLen = _htons(quint16(val.size()));
//qDebug() << "Adding to msg(" << ba.size() << "/" << val.size() << "): " << ba << " -> " << val;
buf.append(reinterpret_cast<const char*>(&keyLen), 2);
buf.append(reinterpret_cast<const char*>(&valLen), 2);
buf.append(ba);
buf.append(val);
}
_bufferSize = quint32(buf.length() + HEADER_LEN);
allocBuffer();
memcpy(_buffer + HEADER_LEN, buf.data(), size_t(buf.size()));
_buffer[0] = 'P';
_buffer[1] = 'V';
_buffer[2] = 'S';
_buffer[3] = '2';
const quint32 flipped = _htonl(_bufferSize - HEADER_LEN);
memcpy(_buffer + 4, &flipped, 4);
}
void NetworkMessage::buildErrorMessage(const QString& error)
{
this->reset();
this->setField(_ID, _ERROR);
this->setField(_ERROR, error);
}