summaryrefslogtreecommitdiffstats
path: root/examples/network/network-chat
diff options
context:
space:
mode:
Diffstat (limited to 'examples/network/network-chat')
-rw-r--r--examples/network/network-chat/client.cpp2
-rw-r--r--examples/network/network-chat/connection.cpp264
-rw-r--r--examples/network/network-chat/connection.h17
-rw-r--r--examples/network/network-chat/peermanager.cpp54
-rw-r--r--examples/network/network-chat/peermanager.h4
-rw-r--r--examples/network/network-chat/server.cpp3
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);
}