/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** 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 either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** 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.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "connection.h" #include 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(); }