1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "private/qpacketprotocol_p.h"
44 #include <QtCore/QBuffer>
45 #include <QtCore/QElapsedTimer>
49 static const unsigned int MAX_PACKET_SIZE = 0x7FFFFFFF;
52 \class QPacketProtocol
55 \brief The QPacketProtocol class encapsulates communicating discrete packets
56 across fragmented IO channels, such as TCP sockets.
58 QPacketProtocol makes it simple to send arbitrary sized data "packets" across
59 fragmented transports such as TCP and UDP.
61 As transmission boundaries are not respected, sending packets over protocols
62 like TCP frequently involves "stitching" them back together at the receiver.
63 QPacketProtocol makes this easier by performing this task for you. Packet
64 data sent using QPacketProtocol is prepended with a 4-byte size header
65 allowing the receiving QPacketProtocol to buffer the packet internally until
66 it has all been received. QPacketProtocol does not perform any sanity
67 checking on the size or on the data, so this class should only be used in
68 prototyping or trusted situations where DOS attacks are unlikely.
70 QPacketProtocol does not perform any communications itself. Instead it can
71 operate on any QIODevice that supports the QIODevice::readyRead() signal. A
72 logical "packet" is encapsulated by the companion QPacket class. The
73 following example shows two ways to send data using QPacketProtocol. The
74 transmitted data is equivalent in both.
78 // ... connect socket ...
80 QPacketProtocol protocol(&socket);
82 // Send packet the quick way
83 protocol.send() << "Hello world" << 123;
85 // Send packet the longer way
87 packet << "Hello world" << 123;
88 protocol.send(packet);
91 Likewise, the following shows how to read data from QPacketProtocol, assuming
92 that the QPacketProtocol::readyRead() signal has been emitted.
95 // ... QPacketProtocol::readyRead() is emitted ...
100 // Receive packet the quick way
101 protocol.read() >> a >> b;
103 // Receive packet the longer way
104 QPacket packet = protocol.read();
112 class QPacketProtocolPrivate : public QObject
116 QPacketProtocolPrivate(QPacketProtocol *parent, QIODevice *_dev)
117 : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE),
118 waitingForPacket(false), dev(_dev)
120 Q_ASSERT(4 == sizeof(qint32));
122 QObject::connect(this, SIGNAL(readyRead()),
123 parent, SIGNAL(readyRead()));
124 QObject::connect(this, SIGNAL(packetWritten()),
125 parent, SIGNAL(packetWritten()));
126 QObject::connect(this, SIGNAL(invalidPacket()),
127 parent, SIGNAL(invalidPacket()));
128 QObject::connect(dev, SIGNAL(readyRead()),
129 this, SLOT(readyToRead()));
130 QObject::connect(dev, SIGNAL(aboutToClose()),
131 this, SLOT(aboutToClose()));
132 QObject::connect(dev, SIGNAL(bytesWritten(qint64)),
133 this, SLOT(bytesWritten(qint64)));
138 void packetWritten();
139 void invalidPacket();
145 sendingPackets.clear();
149 void bytesWritten(qint64 bytes)
151 Q_ASSERT(!sendingPackets.isEmpty());
154 if (sendingPackets.at(0) > bytes) {
155 sendingPackets[0] -= bytes;
158 bytes -= sendingPackets.at(0);
159 sendingPackets.removeFirst();
160 emit packetWritten();
168 // Need to get trailing data
169 if (-1 == inProgressSize) {
170 // We need a size header of sizeof(qint32)
171 if (sizeof(qint32) > (uint)dev->bytesAvailable())
175 int read = dev->read((char *)&inProgressSize, sizeof(qint32));
176 Q_ASSERT(read == sizeof(qint32));
179 // Check sizing constraints
180 if (inProgressSize > maxPacketSize) {
181 QObject::disconnect(dev, SIGNAL(readyRead()),
182 this, SLOT(readyToRead()));
183 QObject::disconnect(dev, SIGNAL(aboutToClose()),
184 this, SLOT(aboutToClose()));
185 QObject::disconnect(dev, SIGNAL(bytesWritten(qint64)),
186 this, SLOT(bytesWritten(qint64)));
188 emit invalidPacket();
192 inProgressSize -= sizeof(qint32);
194 inProgress.append(dev->read(inProgressSize - inProgress.size()));
196 if (inProgressSize == inProgress.size()) {
197 // Packet has arrived!
198 packets.append(inProgress);
202 waitingForPacket = false;
211 QList<qint64> sendingPackets;
212 QList<QByteArray> packets;
213 QByteArray inProgress;
214 qint32 inProgressSize;
215 qint32 maxPacketSize;
216 bool waitingForPacket;
221 Construct a QPacketProtocol instance that works on \a dev with the
224 QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent)
225 : QObject(parent), d(new QPacketProtocolPrivate(this, dev))
231 Destroys the QPacketProtocol instance.
233 QPacketProtocol::~QPacketProtocol()
238 Returns the maximum packet size allowed. By default this is
241 If a packet claiming to be larger than the maximum packet size is received,
242 the QPacketProtocol::invalidPacket() signal is emitted.
244 \sa QPacketProtocol::setMaximumPacketSize()
246 qint32 QPacketProtocol::maximumPacketSize() const
248 return d->maxPacketSize;
252 Sets the maximum allowable packet size to \a max.
254 \sa QPacketProtocol::maximumPacketSize()
256 qint32 QPacketProtocol::setMaximumPacketSize(qint32 max)
258 if (max > (signed)sizeof(qint32))
259 d->maxPacketSize = max;
260 return d->maxPacketSize;
264 Returns a streamable object that is transmitted on destruction. For example
267 protocol.send() << "Hello world" << 123;
270 will send a packet containing "Hello world" and 123. To construct more
271 complex packets, explicitly construct a QPacket instance.
273 QPacketAutoSend QPacketProtocol::send()
275 return QPacketAutoSend(this);
279 \fn void QPacketProtocol::send(const QPacket & packet)
281 Transmit the \a packet.
283 void QPacketProtocol::send(const QPacket & p)
286 return; // We don't send empty packets
288 qint64 sendSize = p.b.size() + sizeof(qint32);
290 d->sendingPackets.append(sendSize);
291 qint32 sendSize32 = sendSize;
292 qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32));
293 Q_ASSERT(writeBytes == sizeof(qint32));
294 writeBytes = d->dev->write(p.b);
295 Q_ASSERT(writeBytes == p.b.size());
299 Returns the number of received packets yet to be read.
301 qint64 QPacketProtocol::packetsAvailable() const
303 return d->packets.count();
307 Discard any unread packets.
309 void QPacketProtocol::clear()
315 Return the next unread packet, or an invalid QPacket instance if no packets
316 are available. This method does NOT block.
318 QPacket QPacketProtocol::read()
320 if (0 == d->packets.count())
323 QPacket rv(d->packets.at(0));
324 d->packets.removeFirst();
329 Returns the difference between msecs and elapsed. If msecs is -1,
330 however, -1 is returned.
332 static int qt_timeout_value(int msecs, int elapsed)
337 int timeout = msecs - elapsed;
338 return timeout < 0 ? 0 : timeout;
342 This function locks until a new packet is available for reading and the
343 \l{QIODevice::}{readyRead()} signal has been emitted. The function
344 will timeout after \a msecs milliseconds; the default timeout is
347 The function returns true if the readyRead() signal is emitted and
348 there is new data available for reading; otherwise it returns false
349 (if an error occurred or the operation timed out).
352 bool QPacketProtocol::waitForReadyRead(int msecs)
354 if (!d->packets.isEmpty())
357 QElapsedTimer stopWatch;
360 d->waitingForPacket = true;
362 if (!d->dev->waitForReadyRead(msecs))
364 if (!d->waitingForPacket)
366 msecs = qt_timeout_value(msecs, stopWatch.elapsed());
371 Return the QIODevice passed to the QPacketProtocol constructor.
373 QIODevice *QPacketProtocol::device()
379 \fn void QPacketProtocol::readyRead()
381 Emitted whenever a new packet is received. Applications may use
382 QPacketProtocol::read() to retrieve this packet.
386 \fn void QPacketProtocol::invalidPacket()
388 A packet larger than the maximum allowable packet size was received. The
389 packet will be discarded and, as it indicates corruption in the protocol, no
390 further packets will be received.
394 \fn void QPacketProtocol::packetWritten()
396 Emitted each time a packet is completing written to the device. This signal
397 may be used for communications flow control.
404 \brief The QPacket class encapsulates an unfragmentable packet of data to be
405 transmitted by QPacketProtocol.
407 The QPacket class works together with QPacketProtocol to make it simple to
408 send arbitrary sized data "packets" across fragmented transports such as TCP
411 QPacket provides a QDataStream interface to an unfragmentable packet.
412 Applications should construct a QPacket, propagate it with data and then
413 transmit it over a QPacketProtocol instance. For example:
415 QPacketProtocol protocol(...);
418 myPacket << "Hello world!" << 123;
419 protocol.send(myPacket);
422 As long as both ends of the connection are using the QPacketProtocol class,
423 the data within this packet will be delivered unfragmented at the other end,
424 ready for extraction.
430 QPacket myPacket = protocol.read();
432 myPacket >> greeting >> count;
435 Only packets returned from QPacketProtocol::read() may be read from. QPacket
436 instances constructed by directly by applications are for transmission only
437 and are considered "write only". Attempting to read data from them will
438 result in undefined behavior.
445 Constructs an empty write-only packet.
448 : QDataStream(), buf(0)
450 buf = new QBuffer(&b);
451 buf->open(QIODevice::WriteOnly);
453 setVersion(QDataStream::Qt_4_7);
457 Destroys the QPacket instance.
468 Creates a copy of \a other. The initial stream positions are shared, but the
469 two packets are otherwise independent.
471 QPacket::QPacket(const QPacket & other)
472 : QDataStream(), b(other.b), buf(0)
474 buf = new QBuffer(&b);
475 buf->open(other.buf->openMode());
482 QPacket::QPacket(const QByteArray & ba)
483 : QDataStream(), b(ba), buf(0)
485 buf = new QBuffer(&b);
486 buf->open(QIODevice::ReadOnly);
491 Returns true if this packet is empty - that is, contains no data.
493 bool QPacket::isEmpty() const
499 Returns raw packet data.
501 QByteArray QPacket::data() const
507 Clears data in the packet. This is useful for reusing one writable packet.
510 QPacketProtocol protocol(...);
514 packet << "Hello world!" << 123;
515 protocol.send(packet);
518 packet << "Goodbyte world!" << 789;
519 protocol.send(packet);
522 void QPacket::clear()
524 QBuffer::OpenMode oldMode = buf->openMode();
527 buf->setBuffer(&b); // reset QBuffer internals with new size of b.
532 \class QPacketAutoSend
537 QPacketAutoSend::QPacketAutoSend(QPacketProtocol *_p)
542 QPacketAutoSend::~QPacketAutoSend()
550 #include <qpacketprotocol.moc>