|  | Home · All Classes · Main Classes · Grouped Classes · Modules · Functions |

# connection.cpp Example File
network/chat/connection.cpp

    /****************************************************************************
    **
    ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
    **
    ** This file is part of the example classes of the Qt Toolkit.
    **
    ** Licensees holding valid Qt Preview licenses may use this file in
    ** accordance with the Qt Preview License Agreement provided with the
    ** Software.
    **
    ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
    ** information about Qt Commercial License Agreements.
    **
    ** Contact info@trolltech.com if any conditions of this licensing are
    ** not clear to you.
    **
    ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    **
    ****************************************************************************/

    #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();
    }