diff options
Diffstat (limited to 'examples/network/network-chat')
-rw-r--r-- | examples/network/network-chat/client.cpp | 2 | ||||
-rw-r--r-- | examples/network/network-chat/connection.cpp | 264 | ||||
-rw-r--r-- | examples/network/network-chat/connection.h | 17 | ||||
-rw-r--r-- | examples/network/network-chat/peermanager.cpp | 54 | ||||
-rw-r--r-- | examples/network/network-chat/peermanager.h | 4 | ||||
-rw-r--r-- | examples/network/network-chat/server.cpp | 3 |
6 files changed, 178 insertions, 166 deletions
diff --git a/examples/network/network-chat/client.cpp b/examples/network/network-chat/client.cpp index c1eda52a0c..97c2c44b6b 100644 --- a/examples/network/network-chat/client.cpp +++ b/examples/network/network-chat/client.cpp @@ -78,7 +78,7 @@ void Client::sendMessage(const QString &message) QString Client::nickName() const { - return QString(peerManager->userName()) + '@' + QHostInfo::localHostName() + return peerManager->userName() + '@' + QHostInfo::localHostName() + ':' + QString::number(server.serverPort()); } diff --git a/examples/network/network-chat/connection.cpp b/examples/network/network-chat/connection.cpp index 332d5dc56b..58cf67eb6d 100644 --- a/examples/network/network-chat/connection.cpp +++ b/examples/network/network-chat/connection.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. @@ -55,17 +56,29 @@ static const int TransferTimeout = 30 * 1000; static const int PongTimeout = 60 * 1000; static const int PingInterval = 5 * 1000; -static const char SeparatorToken = ' '; + +/* + * Protocol is defined as follows, using the CBOR Data Definition Language: + * + * protocol = [ + * greeting, ; must start with a greeting command + * * command ; zero or more regular commands after + * ] + * command = plaintext / ping / pong / greeting + * plaintext = { 0 => text } + * ping = { 1 => null } + * pong = { 2 => null } + * greeting = { 3 => text } + */ Connection::Connection(QObject *parent) - : QTcpSocket(parent) + : QTcpSocket(parent), writer(this) { greetingMessage = tr("undefined"); username = tr("unknown"); state = WaitingForGreeting; currentDataType = Undefined; - numBytesForCurrentDataType = -1; - transferTimerId = 0; + transferTimerId = -1; isGreetingMessageSent = false; pingTimer.setInterval(PingInterval); @@ -76,6 +89,22 @@ Connection::Connection(QObject *parent) this, SLOT(sendGreetingMessage())); } +Connection::Connection(qintptr socketDescriptor, QObject *parent) + : Connection(parent) +{ + setSocketDescriptor(socketDescriptor); + reader.setDevice(this); +} + +Connection::~Connection() +{ + if (isGreetingMessageSent) { + // Indicate clean shutdown. + writer.endArray(); + waitForBytesWritten(2000); + } +} + QString Connection::name() const { return username; @@ -91,9 +120,11 @@ bool Connection::sendMessage(const QString &message) if (message.isEmpty()) return false; - QByteArray msg = message.toUtf8(); - QByteArray data = "MESSAGE " + QByteArray::number(msg.size()) + ' ' + msg; - return write(data) == data.size(); + writer.startMap(1); + writer.append(PlainText); + writer.append(message); + writer.endMap(); + return true; } void Connection::timerEvent(QTimerEvent *timerEvent) @@ -101,61 +132,75 @@ void Connection::timerEvent(QTimerEvent *timerEvent) if (timerEvent->timerId() == transferTimerId) { abort(); killTimer(transferTimerId); - transferTimerId = 0; + transferTimerId = -1; } } void Connection::processReadyRead() { - if (state == WaitingForGreeting) { - if (!readProtocolHeader()) - return; - if (currentDataType != Greeting) { - abort(); - return; + // we've got more data, let's parse + reader.reparse(); + while (reader.lastError() == QCborError::NoError) { + if (state == WaitingForGreeting) { + if (!reader.isArray()) + break; // protocol error + + reader.enterContainer(); // we'll be in this array forever + state = ReadingGreeting; + } else if (reader.containerDepth() == 1) { + // Current state: no command read + // Next state: read command ID + if (!reader.hasNext()) { + reader.leaveContainer(); + disconnectFromHost(); + return; + } + + if (!reader.isMap() || !reader.isLengthKnown() || reader.length() != 1) + break; // protocol error + reader.enterContainer(); + } else if (currentDataType == Undefined) { + // Current state: read command ID + // Next state: read command payload + if (!reader.isInteger()) + break; // protocol error + currentDataType = DataType(reader.toInteger()); + reader.next(); + } else { + // Current state: read command payload + if (reader.isString()) { + auto r = reader.readString(); + buffer += r.data; + if (r.status != QCborStreamReader::EndOfString) + continue; + } else if (reader.isNull()) { + reader.next(); + } else { + break; // protocol error + } + + // Next state: no command read + reader.leaveContainer(); + if (transferTimerId != -1) { + killTimer(transferTimerId); + transferTimerId = -1; + } + + if (state == ReadingGreeting) { + if (currentDataType != Greeting) + break; // protocol error + processGreeting(); + } else { + processData(); + } } - state = ReadingGreeting; } - if (state == ReadingGreeting) { - if (!hasEnoughData()) - return; - - buffer = read(numBytesForCurrentDataType); - if (buffer.size() != numBytesForCurrentDataType) { - abort(); - return; - } - - username = QString(buffer) + '@' + peerAddress().toString() + ':' - + QString::number(peerPort()); - currentDataType = Undefined; - numBytesForCurrentDataType = 0; - buffer.clear(); - - if (!isValid()) { - abort(); - return; - } - - if (!isGreetingMessageSent) - sendGreetingMessage(); - - pingTimer.start(); - pongTime.start(); - state = ReadyForUse; - emit readyForUse(); - } + if (reader.lastError() != QCborError::EndOfFile) + abort(); // parse error - do { - if (currentDataType == Undefined) { - if (!readProtocolHeader()) - return; - } - if (!hasEnoughData()) - return; - processData(); - } while (bytesAvailable() > 0); + if (transferTimerId != -1 && reader.containerDepth() > 1) + transferTimerId = startTimer(TransferTimeout); } void Connection::sendPing() @@ -165,112 +210,58 @@ void Connection::sendPing() return; } - write("PING 1 p"); + writer.startMap(1); + writer.append(Ping); + writer.append(nullptr); // no payload + writer.endMap(); } void Connection::sendGreetingMessage() { - QByteArray greeting = greetingMessage.toUtf8(); - QByteArray data = "GREETING " + QByteArray::number(greeting.size()) + ' ' + greeting; - if (write(data) == data.size()) - isGreetingMessageSent = true; -} + writer.startArray(); // this array never ends -int Connection::readDataIntoBuffer(int maxSize) -{ - if (maxSize > MaxBufferSize) - return 0; + writer.startMap(1); + writer.append(Greeting); + writer.append(greetingMessage); + writer.endMap(); + isGreetingMessageSent = true; - int numBytesBeforeRead = buffer.size(); - if (numBytesBeforeRead == MaxBufferSize) { - abort(); - return 0; - } - - while (bytesAvailable() > 0 && buffer.size() < maxSize) { - buffer.append(read(1)); - if (buffer.endsWith(SeparatorToken)) - break; - } - return buffer.size() - numBytesBeforeRead; + if (!reader.device()) + reader.setDevice(this); } -int Connection::dataLengthForCurrentDataType() +void Connection::processGreeting() { - if (bytesAvailable() <= 0 || readDataIntoBuffer() <= 0 - || !buffer.endsWith(SeparatorToken)) - return 0; - - buffer.chop(1); - int number = buffer.toInt(); + username = buffer + '@' + peerAddress().toString() + ':' + + QString::number(peerPort()); + currentDataType = Undefined; buffer.clear(); - return number; -} - -bool Connection::readProtocolHeader() -{ - if (transferTimerId) { - killTimer(transferTimerId); - transferTimerId = 0; - } - - if (readDataIntoBuffer() <= 0) { - transferTimerId = startTimer(TransferTimeout); - return false; - } - if (buffer == "PING ") { - currentDataType = Ping; - } else if (buffer == "PONG ") { - currentDataType = Pong; - } else if (buffer == "MESSAGE ") { - currentDataType = PlainText; - } else if (buffer == "GREETING ") { - currentDataType = Greeting; - } else { - currentDataType = Undefined; + if (!isValid()) { abort(); - return false; - } - - buffer.clear(); - numBytesForCurrentDataType = dataLengthForCurrentDataType(); - return true; -} - -bool Connection::hasEnoughData() -{ - if (transferTimerId) { - QObject::killTimer(transferTimerId); - transferTimerId = 0; + return; } - if (numBytesForCurrentDataType <= 0) - numBytesForCurrentDataType = dataLengthForCurrentDataType(); - - if (bytesAvailable() < numBytesForCurrentDataType - || numBytesForCurrentDataType <= 0) { - transferTimerId = startTimer(TransferTimeout); - return false; - } + if (!isGreetingMessageSent) + sendGreetingMessage(); - return true; + pingTimer.start(); + pongTime.start(); + state = ReadyForUse; + emit readyForUse(); } void Connection::processData() { - buffer = read(numBytesForCurrentDataType); - if (buffer.size() != numBytesForCurrentDataType) { - abort(); - return; - } - switch (currentDataType) { case PlainText: - emit newMessage(username, QString::fromUtf8(buffer)); + emit newMessage(username, buffer); break; case Ping: - write("PONG 1 p"); + writer.startMap(1); + writer.append(Pong); + writer.append(nullptr); // no payload + writer.endMap(); break; case Pong: pongTime.restart(); @@ -280,6 +271,5 @@ void Connection::processData() } currentDataType = Undefined; - numBytesForCurrentDataType = 0; buffer.clear(); } diff --git a/examples/network/network-chat/connection.h b/examples/network/network-chat/connection.h index 67d25d4d1e..fa0671a522 100644 --- a/examples/network/network-chat/connection.h +++ b/examples/network/network-chat/connection.h @@ -51,10 +51,12 @@ #ifndef CONNECTION_H #define CONNECTION_H +#include <QCborStreamReader> +#include <QCborStreamWriter> +#include <QElapsedTimer> #include <QHostAddress> #include <QString> #include <QTcpSocket> -#include <QTime> #include <QTimer> static const int MaxBufferSize = 1024000; @@ -78,6 +80,8 @@ public: }; Connection(QObject *parent = 0); + Connection(qintptr socketDescriptor, QObject *parent = 0); + ~Connection(); QString name() const; void setGreetingMessage(const QString &message); @@ -96,20 +100,19 @@ private slots: void sendGreetingMessage(); private: - int readDataIntoBuffer(int maxSize = MaxBufferSize); - int dataLengthForCurrentDataType(); - bool readProtocolHeader(); bool hasEnoughData(); + void processGreeting(); void processData(); + QCborStreamReader reader; + QCborStreamWriter writer; QString greetingMessage; QString username; QTimer pingTimer; - QTime pongTime; - QByteArray buffer; + QElapsedTimer pongTime; + QString buffer; ConnectionState state; DataType currentDataType; - int numBytesForCurrentDataType; int transferTimerId; bool isGreetingMessageSent; }; diff --git a/examples/network/network-chat/peermanager.cpp b/examples/network/network-chat/peermanager.cpp index c70cc5e56d..38fa2e8e50 100644 --- a/examples/network/network-chat/peermanager.cpp +++ b/examples/network/network-chat/peermanager.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. @@ -62,16 +63,14 @@ PeerManager::PeerManager(Client *client) { this->client = client; - QStringList envVariables; - envVariables << "USERNAME" << "USER" << "USERDOMAIN" - << "HOSTNAME" << "DOMAINNAME"; + static const char *envVariables[] = { + "USERNAME", "USER", "USERDOMAIN", "HOSTNAME", "DOMAINNAME" + }; - QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); - foreach (QString string, envVariables) { - if (environment.contains(string)) { - username = environment.value(string).toUtf8(); + for (const char *varname : envVariables) { + username = qEnvironmentVariable(varname); + if (!username.isNull()) break; - } } if (username.isEmpty()) @@ -95,7 +94,7 @@ void PeerManager::setServerPort(int port) serverPort = port; } -QByteArray PeerManager::userName() const +QString PeerManager::userName() const { return username; } @@ -108,7 +107,7 @@ void PeerManager::startBroadcasting() bool PeerManager::isLocalHostAddress(const QHostAddress &address) { foreach (QHostAddress localAddress, ipAddresses) { - if (address == localAddress) + if (address.isEqual(localAddress)) return true; } return false; @@ -116,9 +115,14 @@ bool PeerManager::isLocalHostAddress(const QHostAddress &address) void PeerManager::sendBroadcastDatagram() { - QByteArray datagram(username); - datagram.append('@'); - datagram.append(QByteArray::number(serverPort)); + QByteArray datagram; + { + QCborStreamWriter writer(&datagram); + writer.startArray(2); + writer.append(username); + writer.append(serverPort); + writer.endArray(); + } bool validBroadcastAddresses = true; foreach (QHostAddress address, broadcastAddresses) { @@ -142,11 +146,27 @@ void PeerManager::readBroadcastDatagram() &senderIp, &senderPort) == -1) continue; - QList<QByteArray> list = datagram.split('@'); - if (list.size() != 2) - continue; + int senderServerPort; + { + // decode the datagram + QCborStreamReader reader(datagram); + if (reader.lastError() != QCborError::NoError || !reader.isArray()) + continue; + if (!reader.isLengthKnown() || reader.length() != 2) + continue; + + reader.enterContainer(); + if (reader.lastError() != QCborError::NoError || !reader.isString()) + continue; + while (reader.readString().status == QCborStreamReader::Ok) { + // we don't actually need the username right now + } + + if (reader.lastError() != QCborError::NoError || !reader.isUnsignedInteger()) + continue; + senderServerPort = reader.toInteger(); + } - int senderServerPort = list.at(1).toInt(); if (isLocalHostAddress(senderIp) && senderServerPort == serverPort) continue; diff --git a/examples/network/network-chat/peermanager.h b/examples/network/network-chat/peermanager.h index 0bcd67579c..b79028235b 100644 --- a/examples/network/network-chat/peermanager.h +++ b/examples/network/network-chat/peermanager.h @@ -68,7 +68,7 @@ public: PeerManager(Client *client); void setServerPort(int port); - QByteArray userName() const; + QString userName() const; void startBroadcasting(); bool isLocalHostAddress(const QHostAddress &address); @@ -87,7 +87,7 @@ private: QList<QHostAddress> ipAddresses; QUdpSocket broadcastSocket; QTimer broadcastTimer; - QByteArray username; + QString username; int serverPort; }; diff --git a/examples/network/network-chat/server.cpp b/examples/network/network-chat/server.cpp index cc154728d5..b3e4a07f60 100644 --- a/examples/network/network-chat/server.cpp +++ b/examples/network/network-chat/server.cpp @@ -61,7 +61,6 @@ Server::Server(QObject *parent) void Server::incomingConnection(qintptr socketDescriptor) { - Connection *connection = new Connection(this); - connection->setSocketDescriptor(socketDescriptor); + Connection *connection = new Connection(socketDescriptor, this); emit newConnection(connection); } |