summaryrefslogtreecommitdiffstats
path: root/src/shared/networkmessage.cpp
diff options
context:
space:
mode:
authorsr2013-02-04 19:50:31 +0100
committersr2013-02-04 19:50:31 +0100
commit1a5709501f94014d41987b956338bb6424b9f90c (patch)
treed3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src/shared/networkmessage.cpp
parentTest (diff)
downloadpvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.gz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.xz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.zip
Initial commit
Diffstat (limited to 'src/shared/networkmessage.cpp')
-rw-r--r--src/shared/networkmessage.cpp296
1 files changed, 296 insertions, 0 deletions
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 <QtCore>
+#include <QtNetwork>
+#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<QString, QByteArray>::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));
+}