source: trunk/src/declarative/debugger/qpacketprotocol.cpp@ 938

Last change on this file since 938 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 13.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
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.
23**
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.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qpacketprotocol_p.h"
43
44#include <QBuffer>
45
46QT_BEGIN_NAMESPACE
47
48#define MAX_PACKET_SIZE 0x7FFFFFFF
49
50/*!
51 \class QPacketProtocol
52 \internal
53
54 \brief The QPacketProtocol class encapsulates communicating discrete packets
55 across fragmented IO channels, such as TCP sockets.
56
57 QPacketProtocol makes it simple to send arbitrary sized data "packets" across
58 fragmented transports such as TCP and UDP.
59
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.
68
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.
74
75 \code
76 QTcpSocket socket;
77 // ... connect socket ...
78
79 QPacketProtocol protocol(&socket);
80
81 // Send packet the quick way
82 protocol.send() << "Hello world" << 123;
83
84 // Send packet the longer way
85 QPacket packet;
86 packet << "Hello world" << 123;
87 protocol.send(packet);
88 \endcode
89
90 Likewise, the following shows how to read data from QPacketProtocol, assuming
91 that the QPacketProtocol::readyRead() signal has been emitted.
92
93 \code
94 // ... QPacketProtocol::readyRead() is emitted ...
95
96 int a;
97 QByteArray b;
98
99 // Receive packet the quick way
100 protocol.read() >> a >> b;
101
102 // Receive packet the longer way
103 QPacket packet = protocol.read();
104 p >> a >> b;
105 \endcode
106
107 \ingroup io
108 \sa QPacket
109*/
110
111class QPacketProtocolPrivate : public QObject
112{
113Q_OBJECT
114public:
115 QPacketProtocolPrivate(QPacketProtocol * parent, QIODevice * _dev)
116 : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE),
117 dev(_dev)
118 {
119 Q_ASSERT(4 == sizeof(qint32));
120
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)));
133 }
134
135Q_SIGNALS:
136 void readyRead();
137 void packetWritten();
138 void invalidPacket();
139
140public Q_SLOTS:
141 void aboutToClose()
142 {
143 inProgress.clear();
144 sendingPackets.clear();
145 inProgressSize = -1;
146 }
147
148 void bytesWritten(qint64 bytes)
149 {
150 Q_ASSERT(!sendingPackets.isEmpty());
151
152 while(bytes) {
153 if(sendingPackets.at(0) > bytes) {
154 sendingPackets[0] -= bytes;
155 bytes = 0;
156 } else {
157 bytes -= sendingPackets.at(0);
158 sendingPackets.removeFirst();
159 emit packetWritten();
160 }
161 }
162 }
163
164 void readyToRead()
165 {
166 if(-1 == inProgressSize) {
167 // We need a size header of sizeof(qint32)
168 if(sizeof(qint32) > (uint)dev->bytesAvailable())
169 return;
170
171 // Read size header
172 int read = dev->read((char *)&inProgressSize, sizeof(qint32));
173 Q_ASSERT(read == sizeof(qint32));
174 Q_UNUSED(read);
175
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)));
184 dev = 0;
185 emit invalidPacket();
186 return;
187 }
188
189 inProgressSize -= sizeof(qint32);
190
191 // Need to get trailing data
192 readyToRead();
193 } else {
194 inProgress.append(dev->read(inProgressSize - inProgress.size()));
195
196 if(inProgressSize == inProgress.size()) {
197 // Packet has arrived!
198 packets.append(inProgress);
199 inProgressSize = -1;
200 inProgress.clear();
201
202 emit readyRead();
203
204 // Need to get trailing data
205 readyToRead();
206 }
207 }
208 }
209
210public:
211 QList<qint64> sendingPackets;
212 QList<QByteArray> packets;
213 QByteArray inProgress;
214 qint32 inProgressSize;
215 qint32 maxPacketSize;
216 QIODevice * dev;
217};
218
219/*!
220 Construct a QPacketProtocol instance that works on \a dev with the
221 specified \a parent.
222 */
223QPacketProtocol::QPacketProtocol(QIODevice * dev, QObject * parent)
224: QObject(parent), d(new QPacketProtocolPrivate(this, dev))
225{
226 Q_ASSERT(dev);
227}
228
229/*!
230 Destroys the QPacketProtocol instance.
231 */
232QPacketProtocol::~QPacketProtocol()
233{
234}
235
236/*!
237 Returns the maximum packet size allowed. By default this is
238 2,147,483,647 bytes.
239
240 If a packet claiming to be larger than the maximum packet size is received,
241 the QPacketProtocol::invalidPacket() signal is emitted.
242
243 \sa QPacketProtocol::setMaximumPacketSize()
244 */
245qint32 QPacketProtocol::maximumPacketSize() const
246{
247 return d->maxPacketSize;
248}
249
250/*!
251 Sets the maximum allowable packet size to \a max.
252
253 \sa QPacketProtocol::maximumPacketSize()
254 */
255qint32 QPacketProtocol::setMaximumPacketSize(qint32 max)
256{
257 if(max > (signed)sizeof(qint32))
258 d->maxPacketSize = max;
259 return d->maxPacketSize;
260}
261
262/*!
263 Returns a streamable object that is transmitted on destruction. For example
264
265 \code
266 protocol.send() << "Hello world" << 123;
267 \endcode
268
269 will send a packet containing "Hello world" and 123. To construct more
270 complex packets, explicitly construct a QPacket instance.
271 */
272QPacketAutoSend QPacketProtocol::send()
273{
274 return QPacketAutoSend(this);
275}
276
277/*!
278 \fn void QPacketProtocol::send(const QPacket & packet)
279
280 Transmit the \a packet.
281 */
282void QPacketProtocol::send(const QPacket & p)
283{
284 if(p.b.isEmpty())
285 return; // We don't send empty packets
286
287 qint64 sendSize = p.b.size() + sizeof(qint32);
288
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());
295}
296
297/*!
298 Returns the number of received packets yet to be read.
299 */
300qint64 QPacketProtocol::packetsAvailable() const
301{
302 return d->packets.count();
303}
304
305/*!
306 Discard any unread packets.
307 */
308void QPacketProtocol::clear()
309{
310 d->packets.clear();
311}
312
313/*!
314 Return the next unread packet, or an invalid QPacket instance if no packets
315 are available. This method does NOT block.
316 */
317QPacket QPacketProtocol::read()
318{
319 if(0 == d->packets.count())
320 return QPacket();
321
322 QPacket rv(d->packets.at(0));
323 d->packets.removeFirst();
324 return rv;
325}
326
327/*!
328 Return the QIODevice passed to the QPacketProtocol constructor.
329*/
330QIODevice * QPacketProtocol::device()
331{
332 return d->dev;
333}
334
335/*!
336 \fn void QPacketProtocol::readyRead()
337
338 Emitted whenever a new packet is received. Applications may use
339 QPacketProtocol::read() to retrieve this packet.
340 */
341
342/*!
343 \fn void QPacketProtocol::invalidPacket()
344
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.
348 */
349
350/*!
351 \fn void QPacketProtocol::packetWritten()
352
353 Emitted each time a packet is completing written to the device. This signal
354 may be used for communications flow control.
355 */
356
357/*!
358 \class QPacket
359 \internal
360
361 \brief The QPacket class encapsulates an unfragmentable packet of data to be
362 transmitted by QPacketProtocol.
363
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
366 and UDP.
367
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:
371 \code
372 QPacketProtocol protocol(...);
373
374 QPacket myPacket;
375 myPacket << "Hello world!" << 123;
376 protocol.send(myPacket);
377 \endcode
378
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.
382
383 \code
384 QByteArray greeting;
385 int count;
386
387 QPacket myPacket = protocol.read();
388
389 myPacket >> greeting >> count;
390 \endcode
391
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.
396
397 \ingroup io
398 \sa QPacketProtocol
399 */
400
401/*!
402 Constructs an empty write-only packet.
403 */
404QPacket::QPacket()
405: QDataStream(), buf(0)
406{
407 buf = new QBuffer(&b);
408 buf->open(QIODevice::WriteOnly);
409 setDevice(buf);
410}
411
412/*!
413 Destroys the QPacket instance.
414 */
415QPacket::~QPacket()
416{
417 if(buf) {
418 delete buf;
419 buf = 0;
420 }
421}
422
423/*!
424 Creates a copy of \a other. The initial stream positions are shared, but the
425 two packets are otherwise independent.
426 */
427QPacket::QPacket(const QPacket & other)
428: QDataStream(), b(other.b), buf(0)
429{
430 buf = new QBuffer(&b);
431 buf->open(other.buf->openMode());
432 setDevice(buf);
433}
434
435/*!
436 \internal
437 */
438QPacket::QPacket(const QByteArray & ba)
439: QDataStream(), b(ba), buf(0)
440{
441 buf = new QBuffer(&b);
442 buf->open(QIODevice::ReadOnly);
443 setDevice(buf);
444}
445
446/*!
447 Returns true if this packet is empty - that is, contains no data.
448 */
449bool QPacket::isEmpty() const
450{
451 return b.isEmpty();
452}
453
454/*!
455 Returns raw packet data.
456 */
457QByteArray QPacket::data() const
458{
459 return b;
460}
461
462/*!
463 Clears data in the packet. This is useful for reusing one writable packet.
464 For example
465 \code
466 QPacketProtocol protocol(...);
467
468 QPacket packet;
469
470 packet << "Hello world!" << 123;
471 protocol.send(packet);
472
473 packet.clear();
474 packet << "Goodbyte world!" << 789;
475 protocol.send(packet);
476 \endcode
477 */
478void QPacket::clear()
479{
480 QBuffer::OpenMode oldMode = buf->openMode();
481 buf->close();
482 b.clear();
483 buf->setBuffer(&b); // reset QBuffer internals with new size of b.
484 buf->open(oldMode);
485}
486
487/*!
488 \class QPacketAutoSend
489 \internal
490
491 \internal
492 */
493QPacketAutoSend::QPacketAutoSend(QPacketProtocol * _p)
494: QPacket(), p(_p)
495{
496}
497
498QPacketAutoSend::~QPacketAutoSend()
499{
500 if(!b.isEmpty())
501 p->send(*this);
502}
503
504QT_END_NAMESPACE
505
506#include <qpacketprotocol.moc>
Note: See TracBrowser for help on using the repository browser.