1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qpacketprotocol.h"
44 #include <QtCore/QBuffer>
45 #include <QtCore/QElapsedTimer>
47 static const unsigned int MAX_PACKET_SIZE = 0x7FFFFFFF;
50 \class QPacketProtocol
53 \brief The QPacketProtocol class encapsulates communicating discrete packets
54 across fragmented IO channels, such as TCP sockets.
56 QPacketProtocol makes it simple to send arbitrary sized data "packets" across
57 fragmented transports such as TCP and UDP.
59 As transmission boundaries are not respected, sending packets over protocols
60 like TCP frequently involves "stitching" them back together at the receiver.
61 QPacketProtocol makes this easier by performing this task for you. Packet
62 data sent using QPacketProtocol is prepended with a 4-byte size header
63 allowing the receiving QPacketProtocol to buffer the packet internally until
64 it has all been received. QPacketProtocol does not perform any sanity
65 checking on the size or on the data, so this class should only be used in
66 prototyping or trusted situations where DOS attacks are unlikely.
68 QPacketProtocol does not perform any communications itself. Instead it can
69 operate on any QIODevice that supports the QIODevice::readyRead() signal. A
70 logical "packet" is encapsulated by the companion QPacket class. The
71 following example shows two ways to send data using QPacketProtocol. The
72 transmitted data is equivalent in both.
76 // ... connect socket ...
78 QPacketProtocol protocol(&socket);
80 // Send packet the quick way
81 protocol.send() << "Hello world" << 123;
83 // Send packet the longer way
85 packet << "Hello world" << 123;
86 protocol.send(packet);
89 Likewise, the following shows how to read data from QPacketProtocol, assuming
90 that the QPacketProtocol::readyRead() signal has been emitted.
93 // ... QPacketProtocol::readyRead() is emitted ...
98 // Receive packet the quick way
99 protocol.read() >> a >> b;
101 // Receive packet the longer way
102 QPacket packet = protocol.read();
110 class QPacketProtocolPrivate : public QObject
114 QPacketProtocolPrivate(QPacketProtocol *parent, QIODevice *_dev)
115 : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE),
116 waitingForPacket(false), dev(_dev)
118 Q_ASSERT(4 == sizeof(qint32));
120 QObject::connect(this, SIGNAL(readyRead()),
121 parent, SIGNAL(readyRead()));
122 QObject::connect(this, SIGNAL(packetWritten()),
123 parent, SIGNAL(packetWritten()));
124 QObject::connect(this, SIGNAL(invalidPacket()),
125 parent, SIGNAL(invalidPacket()));
126 QObject::connect(dev, SIGNAL(readyRead()),
127 this, SLOT(readyToRead()));
128 QObject::connect(dev, SIGNAL(aboutToClose()),
129 this, SLOT(aboutToClose()));
130 QObject::connect(dev, SIGNAL(bytesWritten(qint64)),
131 this, SLOT(bytesWritten(qint64)));
136 void packetWritten();
137 void invalidPacket();
143 sendingPackets.clear();
147 void bytesWritten(qint64 bytes)
149 Q_ASSERT(!sendingPackets.isEmpty());
152 if (sendingPackets.at(0) > bytes) {
153 sendingPackets[0] -= bytes;
156 bytes -= sendingPackets.at(0);
157 sendingPackets.removeFirst();
158 emit packetWritten();
166 // Need to get trailing data
167 if (-1 == inProgressSize) {
168 // We need a size header of sizeof(qint32)
169 if (sizeof(qint32) > (uint)dev->bytesAvailable())
173 int read = dev->read((char *)&inProgressSize, sizeof(qint32));
174 Q_ASSERT(read == sizeof(qint32));
177 // Check sizing constraints
178 if (inProgressSize > maxPacketSize) {
179 QObject::disconnect(dev, SIGNAL(readyRead()),
180 this, SLOT(readyToRead()));
181 QObject::disconnect(dev, SIGNAL(aboutToClose()),
182 this, SLOT(aboutToClose()));
183 QObject::disconnect(dev, SIGNAL(bytesWritten(qint64)),
184 this, SLOT(bytesWritten(qint64)));
186 emit invalidPacket();
190 inProgressSize -= sizeof(qint32);
192 inProgress.append(dev->read(inProgressSize - inProgress.size()));
194 if (inProgressSize == inProgress.size()) {
195 // Packet has arrived!
196 packets.append(inProgress);
200 waitingForPacket = false;
209 QList<qint64> sendingPackets;
210 QList<QByteArray> packets;
211 QByteArray inProgress;
212 qint32 inProgressSize;
213 qint32 maxPacketSize;
214 bool waitingForPacket;
219 Construct a QPacketProtocol instance that works on \a dev with the
222 QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent)
223 : QObject(parent), d(new QPacketProtocolPrivate(this, dev))
229 Destroys the QPacketProtocol instance.
231 QPacketProtocol::~QPacketProtocol()
236 Returns the maximum packet size allowed. By default this is
239 If a packet claiming to be larger than the maximum packet size is received,
240 the QPacketProtocol::invalidPacket() signal is emitted.
242 \sa QPacketProtocol::setMaximumPacketSize()
244 qint32 QPacketProtocol::maximumPacketSize() const
246 return d->maxPacketSize;
250 Sets the maximum allowable packet size to \a max.
252 \sa QPacketProtocol::maximumPacketSize()
254 qint32 QPacketProtocol::setMaximumPacketSize(qint32 max)
256 if (max > (signed)sizeof(qint32))
257 d->maxPacketSize = max;
258 return d->maxPacketSize;
262 Returns a streamable object that is transmitted on destruction. For example
265 protocol.send() << "Hello world" << 123;
268 will send a packet containing "Hello world" and 123. To construct more
269 complex packets, explicitly construct a QPacket instance.
271 QPacketAutoSend QPacketProtocol::send()
273 return QPacketAutoSend(this);
277 \fn void QPacketProtocol::send(const QPacket & packet)
279 Transmit the \a packet.
281 void QPacketProtocol::send(const QPacket & p)
284 return; // We don't send empty packets
286 qint64 sendSize = p.b.size() + sizeof(qint32);
288 d->sendingPackets.append(sendSize);
289 qint32 sendSize32 = sendSize;
290 qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32));
291 Q_ASSERT(writeBytes == sizeof(qint32));
292 writeBytes = d->dev->write(p.b);
293 Q_ASSERT(writeBytes == p.b.size());
297 Returns the number of received packets yet to be read.
299 qint64 QPacketProtocol::packetsAvailable() const
301 return d->packets.count();
305 Discard any unread packets.
307 void QPacketProtocol::clear()
313 Return the next unread packet, or an invalid QPacket instance if no packets
314 are available. This method does NOT block.
316 QPacket QPacketProtocol::read()
318 if (0 == d->packets.count())
321 QPacket rv(d->packets.at(0));
322 d->packets.removeFirst();
327 Returns the difference between msecs and elapsed. If msecs is -1,
328 however, -1 is returned.
330 static int qt_timeout_value(int msecs, int elapsed)
335 int timeout = msecs - elapsed;
336 return timeout < 0 ? 0 : timeout;
340 This function locks until a new packet is available for reading and the
341 \l{QIODevice::}{readyRead()} signal has been emitted. The function
342 will timeout after \a msecs milliseconds; the default timeout is
345 The function returns true if the readyRead() signal is emitted and
346 there is new data available for reading; otherwise it returns false
347 (if an error occurred or the operation timed out).
350 bool QPacketProtocol::waitForReadyRead(int msecs)
352 if (!d->packets.isEmpty())
355 QElapsedTimer stopWatch;
358 d->waitingForPacket = true;
360 if (!d->dev->waitForReadyRead(msecs))
362 if (!d->waitingForPacket)
364 msecs = qt_timeout_value(msecs, stopWatch.elapsed());
369 Return the QIODevice passed to the QPacketProtocol constructor.
371 QIODevice *QPacketProtocol::device()
377 \fn void QPacketProtocol::readyRead()
379 Emitted whenever a new packet is received. Applications may use
380 QPacketProtocol::read() to retrieve this packet.
384 \fn void QPacketProtocol::invalidPacket()
386 A packet larger than the maximum allowable packet size was received. The
387 packet will be discarded and, as it indicates corruption in the protocol, no
388 further packets will be received.
392 \fn void QPacketProtocol::packetWritten()
394 Emitted each time a packet is completing written to the device. This signal
395 may be used for communications flow control.
402 \brief The QPacket class encapsulates an unfragmentable packet of data to be
403 transmitted by QPacketProtocol.
405 The QPacket class works together with QPacketProtocol to make it simple to
406 send arbitrary sized data "packets" across fragmented transports such as TCP
409 QPacket provides a QDataStream interface to an unfragmentable packet.
410 Applications should construct a QPacket, propagate it with data and then
411 transmit it over a QPacketProtocol instance. For example:
413 QPacketProtocol protocol(...);
416 myPacket << "Hello world!" << 123;
417 protocol.send(myPacket);
420 As long as both ends of the connection are using the QPacketProtocol class,
421 the data within this packet will be delivered unfragmented at the other end,
422 ready for extraction.
428 QPacket myPacket = protocol.read();
430 myPacket >> greeting >> count;
433 Only packets returned from QPacketProtocol::read() may be read from. QPacket
434 instances constructed by directly by applications are for transmission only
435 and are considered "write only". Attempting to read data from them will
436 result in undefined behavior.
443 Constructs an empty write-only packet.
446 : QDataStream(), buf(0)
448 buf = new QBuffer(&b);
449 buf->open(QIODevice::WriteOnly);
451 setVersion(QDataStream::Qt_4_7);
455 Destroys the QPacket instance.
466 Creates a copy of \a other. The initial stream positions are shared, but the
467 two packets are otherwise independent.
469 QPacket::QPacket(const QPacket & other)
470 : QDataStream(), b(other.b), buf(0)
472 buf = new QBuffer(&b);
473 buf->open(other.buf->openMode());
480 QPacket::QPacket(const QByteArray & ba)
481 : QDataStream(), b(ba), buf(0)
483 buf = new QBuffer(&b);
484 buf->open(QIODevice::ReadOnly);
489 Returns true if this packet is empty - that is, contains no data.
491 bool QPacket::isEmpty() const
497 Returns raw packet data.
499 QByteArray QPacket::data() const
505 Clears data in the packet. This is useful for reusing one writable packet.
508 QPacketProtocol protocol(...);
512 packet << "Hello world!" << 123;
513 protocol.send(packet);
516 packet << "Goodbyte world!" << 789;
517 protocol.send(packet);
520 void QPacket::clear()
522 QBuffer::OpenMode oldMode = buf->openMode();
525 buf->setBuffer(&b); // reset QBuffer internals with new size of b.
530 \class QPacketAutoSend
535 QPacketAutoSend::QPacketAutoSend(QPacketProtocol *_p)
540 QPacketAutoSend::~QPacketAutoSend()
546 #include <qpacketprotocol.moc>