1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtQml module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
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>