diff options
Diffstat (limited to 'examples/network/network-chat/connection.cpp')
-rw-r--r-- | examples/network/network-chat/connection.cpp | 264 |
1 files changed, 127 insertions, 137 deletions
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(); } |