examples/network/network-chat/connection.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/network/network-chat/connection.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,276 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "connection.h"
+
+#include <QtNetwork>
+
+static const int TransferTimeout = 30 * 1000;
+static const int PongTimeout = 60 * 1000;
+static const int PingInterval = 5 * 1000;
+static const char SeparatorToken = ' ';
+
+Connection::Connection(QObject *parent)
+    : QTcpSocket(parent)
+{
+    greetingMessage = tr("undefined");
+    username = tr("unknown");
+    state = WaitingForGreeting;
+    currentDataType = Undefined;
+    numBytesForCurrentDataType = -1;
+    transferTimerId = 0;
+    isGreetingMessageSent = false;
+    pingTimer.setInterval(PingInterval);
+
+    QObject::connect(this, SIGNAL(readyRead()), this, SLOT(processReadyRead()));
+    QObject::connect(this, SIGNAL(disconnected()), &pingTimer, SLOT(stop()));
+    QObject::connect(&pingTimer, SIGNAL(timeout()), this, SLOT(sendPing()));
+    QObject::connect(this, SIGNAL(connected()),
+                     this, SLOT(sendGreetingMessage()));
+}
+
+QString Connection::name() const
+{
+    return username;
+}
+
+void Connection::setGreetingMessage(const QString &message)
+{
+    greetingMessage = message;
+}
+
+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();
+}
+
+void Connection::timerEvent(QTimerEvent *timerEvent)
+{
+    if (timerEvent->timerId() == transferTimerId) {
+        abort();
+        killTimer(transferTimerId);
+        transferTimerId = 0;
+    }
+}
+
+void Connection::processReadyRead()
+{
+    if (state == WaitingForGreeting) {
+        if (!readProtocolHeader())
+            return;
+        if (currentDataType != Greeting) {
+            abort();
+            return;
+        }
+        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();
+    }
+
+    do {
+        if (currentDataType == Undefined) {
+            if (!readProtocolHeader())
+                return;
+        }
+        if (!hasEnoughData())
+            return;
+        processData();
+    } while (bytesAvailable() > 0);
+}
+
+void Connection::sendPing()
+{
+    if (pongTime.elapsed() > PongTimeout) {
+        abort();
+        return;
+    }
+
+    write("PING 1 p");
+}
+
+void Connection::sendGreetingMessage()
+{
+    QByteArray greeting = greetingMessage.toUtf8();
+    QByteArray data = "GREETING " + QByteArray::number(greeting.size()) + " " + greeting;
+    if (write(data) == data.size())
+        isGreetingMessageSent = true;
+}
+
+int Connection::readDataIntoBuffer(int maxSize)
+{
+    if (maxSize > MaxBufferSize)
+        return 0;
+
+    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;
+}
+
+int Connection::dataLengthForCurrentDataType()
+{
+    if (bytesAvailable() <= 0 || readDataIntoBuffer() <= 0
+            || !buffer.endsWith(SeparatorToken))
+        return 0;
+
+    buffer.chop(1);
+    int number = buffer.toInt();
+    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;
+        abort();
+        return false;
+    }
+
+    buffer.clear();
+    numBytesForCurrentDataType = dataLengthForCurrentDataType();
+    return true;
+}
+
+bool Connection::hasEnoughData()
+{
+    if (transferTimerId) {
+        QObject::killTimer(transferTimerId);
+        transferTimerId = 0;
+    }
+
+    if (numBytesForCurrentDataType <= 0)
+        numBytesForCurrentDataType = dataLengthForCurrentDataType();
+
+    if (bytesAvailable() < numBytesForCurrentDataType
+            || numBytesForCurrentDataType <= 0) {
+        transferTimerId = startTimer(TransferTimeout);
+        return false;
+    }
+
+    return true;
+}
+
+void Connection::processData()
+{
+    buffer = read(numBytesForCurrentDataType);
+    if (buffer.size() != numBytesForCurrentDataType) {
+        abort();
+        return;
+    }
+
+    switch (currentDataType) {
+    case PlainText:
+        emit newMessage(username, QString::fromUtf8(buffer));
+        break;
+    case Ping:
+        write("PONG 1 p");
+        break;
+    case Pong:
+        pongTime.restart();
+        break;
+    default:
+        break;
+    }
+
+    currentDataType = Undefined;
+    numBytesForCurrentDataType = 0;
+    buffer.clear();
+}