From 1a5709501f94014d41987b956338bb6424b9f90c Mon Sep 17 00:00:00 2001 From: sr Date: Mon, 4 Feb 2013 19:50:31 +0100 Subject: Initial commit --- src/shared/networkmessage.cpp | 296 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 src/shared/networkmessage.cpp (limited to 'src/shared/networkmessage.cpp') diff --git a/src/shared/networkmessage.cpp b/src/shared/networkmessage.cpp new file mode 100644 index 0000000..9a7ba9f --- /dev/null +++ b/src/shared/networkmessage.cpp @@ -0,0 +1,296 @@ +/* + * NetworkMessage.cpp + * + * Created on: 18.01.2013 + * Author: sr + */ + +#include +#include +#include "networkmessage.h" + +#define HEADER_LEN 8 +#define MAX_MSG_LEN 60000 + +#define BYTE_SWAP4(x) \ + (((x & 0xFF000000) >> 24) | \ + ((x & 0x00FF0000) >> 8) | \ + ((x & 0x0000FF00) << 8) | \ + ((x & 0x000000FF) << 24)) + +#define BYTE_SWAP2(x) \ + (((x & 0xFF00) >> 8) | \ + ((x & 0x00FF) << 8)) + +static quint16 _htons(quint16 x) +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return x; + return BYTE_SWAP2(x); +} + +static quint16 _ntohs(quint16 x) +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return x; + return BYTE_SWAP2(x); +} + +static quint32 _htonl(quint32 x) +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return x; + return BYTE_SWAP4(x); +} + +static quint32 _ntohl(quint32 x) +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return x; + return BYTE_SWAP4(x); +} + +/* + // ####################################################### \\ ° . ° + \\ ####################################################### // \___/ + */ + +NetworkMessage::NetworkMessage() : + _buffer(NULL), _bufferSize(0), _bufferPos(0), _lastBufferSize(0), _mode(0) +{ + // +} + +NetworkMessage::~NetworkMessage() +{ + if (_buffer) + delete[] _buffer; +} + +inline void NetworkMessage::allocBuffer() +{ + if (_lastBufferSize < _bufferSize || _buffer == NULL) + { + if (_buffer) + delete[] _buffer; + _lastBufferSize = _bufferSize; + _buffer = new char[_lastBufferSize]; + } +} + +bool 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 false; + } + _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 true; + 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 false; + } + if (!this->parseHeader(header)) + return false; + //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) + return false; + _bufferPos += ret; + //qDebug() << "Buffer has now " << _bufferPos << " of " << _bufferSize << " bytes"; + } + if (_bufferSize == _bufferPos) + { + if (!this->parseMessage(_buffer)) + return false; + } + } + return true; +} + +bool 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 false; + } + _mode = 1; + } + if (len < HEADER_LEN) + { + qDebug("UDP message shorter than 8 bytes. ignored."); + return false; + } + if (!this->parseHeader(data)) + return false; + if (len != _bufferSize + HEADER_LEN) + { + qDebug("UDP packet has wrong size. Is %d, expected %d", (int)_bufferSize, len - HEADER_LEN); + return false; + } + return this->parseMessage(data + HEADER_LEN); +} + +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(*(quint32*)(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(*(quint16*)(ptr)); + ptr += 2; + const quint16 valLen = _ntohs(*(quint16*)(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(QString::fromUtf8(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* 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) + return false; + _bufferPos += 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 ? _lastBufferSize : 200); + for (QHash::const_iterator it = _fields.begin(); it != _fields.end(); ++it) + { + const QByteArray &ba = it.key().toUtf8(); + 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((char*)&keyLen, 2); + buf.append((char*)&valLen, 2); + buf.append(ba); + buf.append(val); + } + _bufferSize = buf.length() + HEADER_LEN; + allocBuffer(); + memcpy(_buffer + HEADER_LEN, buf.data(), 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); +} + +void NetworkMessage::buildErrorMessage(const char* error) +{ + this->buildErrorMessage(QString::fromUtf8(error)); +} -- cgit v1.2.3-55-g7522