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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qpacketprotocol_p.h"
48 #define MAX_PACKET_SIZE 0x7FFFFFFF
51 \class QPacketProtocol
54 \brief The QPacketProtocol class encapsulates communicating discrete packets
55 across fragmented IO channels, such as TCP sockets.
57 QPacketProtocol makes it simple to send arbitrary sized data "packets" across
58 fragmented transports such as TCP and UDP.
60 As transmission boundaries are not respected, sending packets over protocols
61 like TCP frequently involves "stitching" them back together at the receiver.
62 QPacketProtocol makes this easier by performing this task for you. Packet
63 data sent using QPacketProtocol is prepended with a 4-byte size header
64 allowing the receiving QPacketProtocol to buffer the packet internally until
65 it has all been received. QPacketProtocol does not perform any sanity
66 checking on the size or on the data, so this class should only be used in
67 prototyping or trusted situations where DOS attacks are unlikely.
69 QPacketProtocol does not perform any communications itself. Instead it can
70 operate on any QIODevice that supports the QIODevice::readyRead() signal. A
71 logical "packet" is encapsulated by the companion QPacket class. The
72 following example shows two ways to send data using QPacketProtocol. The
73 transmitted data is equivalent in both.
77 // ... connect socket ...
79 QPacketProtocol protocol(&socket);
81 // Send packet the quick way
82 protocol.send() << "Hello world" << 123;
84 // Send packet the longer way
86 packet << "Hello world" << 123;
87 protocol.send(packet);
90 Likewise, the following shows how to read data from QPacketProtocol, assuming
91 that the QPacketProtocol::readyRead() signal has been emitted.
94 // ... QPacketProtocol::readyRead() is emitted ...
99 // Receive packet the quick way
100 protocol.read() >> a >> b;
102 // Receive packet the longer way
103 QPacket packet = protocol.read();
111 class QPacketProtocolPrivate : public QObject
115 QPacketProtocolPrivate(QPacketProtocol * parent, QIODevice * _dev)
116 : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE),
119 Q_ASSERT(4 == sizeof(qint32));
121 QObject::connect(this, SIGNAL(readyRead()),
122 parent, SIGNAL(readyRead()));
123 QObject::connect(this, SIGNAL(packetWritten()),
124 parent, SIGNAL(packetWritten()));
125 QObject::connect(this, SIGNAL(invalidPacket()),
126 parent, SIGNAL(invalidPacket()));
127 QObject::connect(dev, SIGNAL(readyRead()),
128 this, SLOT(readyToRead()), Qt::QueuedConnection);
129 QObject::connect(dev, SIGNAL(aboutToClose()),
130 this, SLOT(aboutToClose()));
131 QObject::connect(dev, SIGNAL(bytesWritten(qint64)),
132 this, SLOT(bytesWritten(qint64)));
137 void packetWritten();
138 void invalidPacket();
144 sendingPackets.clear();
148 void bytesWritten(qint64 bytes)
150 Q_ASSERT(!sendingPackets.isEmpty());
153 if(sendingPackets.at(0) > bytes) {
154 sendingPackets[0] -= bytes;
157 bytes -= sendingPackets.at(0);
158 sendingPackets.removeFirst();
159 emit packetWritten();
166 if(-1 == inProgressSize) {
167 // We need a size header of sizeof(qint32)
168 if(sizeof(qint32) > (uint)dev->bytesAvailable())
172 int read = dev->read((char *)&inProgressSize, sizeof(qint32));
173 Q_ASSERT(read == sizeof(qint32));
176 // Check sizing constraints
177 if(inProgressSize > maxPacketSize) {
178 QObject::disconnect(dev, SIGNAL(readyRead()),
179 this, SLOT(readyToRead()));
180 QObject::disconnect(dev, SIGNAL(aboutToClose()),
181 this, SLOT(aboutToClose()));
182 QObject::disconnect(dev, SIGNAL(bytesWritten(qint64)),
183 this, SLOT(bytesWritten(qint64)));
185 emit invalidPacket();
189 inProgressSize -= sizeof(qint32);
191 // Need to get trailing data
194 inProgress.append(dev->read(inProgressSize - inProgress.size()));
196 if(inProgressSize == inProgress.size()) {
197 // Packet has arrived!
198 packets.append(inProgress);
204 // Need to get trailing data
211 QList<qint64> sendingPackets;
212 QList<QByteArray> packets;
213 QByteArray inProgress;
214 qint32 inProgressSize;
215 qint32 maxPacketSize;
220 Construct a QPacketProtocol instance that works on \a dev with the
223 QPacketProtocol::QPacketProtocol(QIODevice * dev, QObject * parent)
224 : QObject(parent), d(new QPacketProtocolPrivate(this, dev))
230 Destroys the QPacketProtocol instance.
232 QPacketProtocol::~QPacketProtocol()
237 Returns the maximum packet size allowed. By default this is
240 If a packet claiming to be larger than the maximum packet size is received,
241 the QPacketProtocol::invalidPacket() signal is emitted.
243 \sa QPacketProtocol::setMaximumPacketSize()
245 qint32 QPacketProtocol::maximumPacketSize() const
247 return d->maxPacketSize;
251 Sets the maximum allowable packet size to \a max.
253 \sa QPacketProtocol::maximumPacketSize()
255 qint32 QPacketProtocol::setMaximumPacketSize(qint32 max)
257 if(max > (signed)sizeof(qint32))
258 d->maxPacketSize = max;
259 return d->maxPacketSize;
263 Returns a streamable object that is transmitted on destruction. For example
266 protocol.send() << "Hello world" << 123;
269 will send a packet containing "Hello world" and 123. To construct more
270 complex packets, explicitly construct a QPacket instance.
272 QPacketAutoSend QPacketProtocol::send()
274 return QPacketAutoSend(this);
278 \fn void QPacketProtocol::send(const QPacket & packet)
280 Transmit the \a packet.
282 void QPacketProtocol::send(const QPacket & p)
285 return; // We don't send empty packets
287 qint64 sendSize = p.b.size() + sizeof(qint32);
289 d->sendingPackets.append(sendSize);
290 qint32 sendSize32 = sendSize;
291 qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32));
292 Q_ASSERT(writeBytes == sizeof(qint32));
293 writeBytes = d->dev->write(p.b);
294 Q_ASSERT(writeBytes == p.b.size());
298 Returns the number of received packets yet to be read.
300 qint64 QPacketProtocol::packetsAvailable() const
302 return d->packets.count();
306 Discard any unread packets.
308 void QPacketProtocol::clear()
314 Return the next unread packet, or an invalid QPacket instance if no packets
315 are available. This method does NOT block.
317 QPacket QPacketProtocol::read()
319 if(0 == d->packets.count())
322 QPacket rv(d->packets.at(0));
323 d->packets.removeFirst();
328 Return the QIODevice passed to the QPacketProtocol constructor.
330 QIODevice * QPacketProtocol::device()
336 \fn void QPacketProtocol::readyRead()
338 Emitted whenever a new packet is received. Applications may use
339 QPacketProtocol::read() to retrieve this packet.
343 \fn void QPacketProtocol::invalidPacket()
345 A packet larger than the maximum allowable packet size was received. The
346 packet will be discarded and, as it indicates corruption in the protocol, no
347 further packets will be received.
351 \fn void QPacketProtocol::packetWritten()
353 Emitted each time a packet is completing written to the device. This signal
354 may be used for communications flow control.
361 \brief The QPacket class encapsulates an unfragmentable packet of data to be
362 transmitted by QPacketProtocol.
364 The QPacket class works together with QPacketProtocol to make it simple to
365 send arbitrary sized data "packets" across fragmented transports such as TCP
368 QPacket provides a QDataStream interface to an unfragmentable packet.
369 Applications should construct a QPacket, propagate it with data and then
370 transmit it over a QPacketProtocol instance. For example:
372 QPacketProtocol protocol(...);
375 myPacket << "Hello world!" << 123;
376 protocol.send(myPacket);
379 As long as both ends of the connection are using the QPacketProtocol class,
380 the data within this packet will be delivered unfragmented at the other end,
381 ready for extraction.
387 QPacket myPacket = protocol.read();
389 myPacket >> greeting >> count;
392 Only packets returned from QPacketProtocol::read() may be read from. QPacket
393 instances constructed by directly by applications are for transmission only
394 and are considered "write only". Attempting to read data from them will
395 result in undefined behavior.
402 Constructs an empty write-only packet.
405 : QDataStream(), buf(0)
407 buf = new QBuffer(&b);
408 buf->open(QIODevice::WriteOnly);
410 setVersion(QDataStream::Qt_4_7);
414 Destroys the QPacket instance.
425 Creates a copy of \a other. The initial stream positions are shared, but the
426 two packets are otherwise independent.
428 QPacket::QPacket(const QPacket & other)
429 : QDataStream(), b(other.b), buf(0)
431 buf = new QBuffer(&b);
432 buf->open(other.buf->openMode());
439 QPacket::QPacket(const QByteArray & ba)
440 : QDataStream(), b(ba), buf(0)
442 buf = new QBuffer(&b);
443 buf->open(QIODevice::ReadOnly);
448 Returns true if this packet is empty - that is, contains no data.
450 bool QPacket::isEmpty() const
456 Returns raw packet data.
458 QByteArray QPacket::data() const
464 Clears data in the packet. This is useful for reusing one writable packet.
467 QPacketProtocol protocol(...);
471 packet << "Hello world!" << 123;
472 protocol.send(packet);
475 packet << "Goodbyte world!" << 789;
476 protocol.send(packet);
479 void QPacket::clear()
481 QBuffer::OpenMode oldMode = buf->openMode();
484 buf->setBuffer(&b); // reset QBuffer internals with new size of b.
489 \class QPacketAutoSend
494 QPacketAutoSend::QPacketAutoSend(QPacketProtocol * _p)
499 QPacketAutoSend::~QPacketAutoSend()
507 #include <qpacketprotocol.moc>