~ubuntu-branches/ubuntu/saucy/qtdeclarative-opensource-src/saucy

« back to all changes in this revision

Viewing changes to tools/qmlprofiler/qpacketprotocol.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 14:17:19 UTC
  • Revision ID: package-import@ubuntu.com-20130205141719-qqeyml8wslpyez52
Tags: upstream-5.0.1
ImportĀ upstreamĀ versionĀ 5.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the QtQml module of the Qt Toolkit.
 
7
**
 
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.
 
16
**
 
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.
 
24
**
 
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.
 
28
**
 
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.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "qpacketprotocol.h"
 
43
 
 
44
#include <QtCore/QBuffer>
 
45
#include <QtCore/QElapsedTimer>
 
46
 
 
47
static const unsigned int MAX_PACKET_SIZE = 0x7FFFFFFF;
 
48
 
 
49
/*!
 
50
  \class QPacketProtocol
 
51
  \internal
 
52
 
 
53
  \brief The QPacketProtocol class encapsulates communicating discrete packets
 
54
  across fragmented IO channels, such as TCP sockets.
 
55
 
 
56
  QPacketProtocol makes it simple to send arbitrary sized data "packets" across
 
57
  fragmented transports such as TCP and UDP.
 
58
 
 
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.
 
67
 
 
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.
 
73
 
 
74
  \code
 
75
  QTcpSocket socket;
 
76
  // ... connect socket ...
 
77
 
 
78
  QPacketProtocol protocol(&socket);
 
79
 
 
80
  // Send packet the quick way
 
81
  protocol.send() << "Hello world" << 123;
 
82
 
 
83
  // Send packet the longer way
 
84
  QPacket packet;
 
85
  packet << "Hello world" << 123;
 
86
  protocol.send(packet);
 
87
  \endcode
 
88
 
 
89
  Likewise, the following shows how to read data from QPacketProtocol, assuming
 
90
  that the QPacketProtocol::readyRead() signal has been emitted.
 
91
 
 
92
  \code
 
93
  // ... QPacketProtocol::readyRead() is emitted ...
 
94
 
 
95
  int a;
 
96
  QByteArray b;
 
97
 
 
98
  // Receive packet the quick way
 
99
  protocol.read() >> a >> b;
 
100
 
 
101
  // Receive packet the longer way
 
102
  QPacket packet = protocol.read();
 
103
  p >> a >> b;
 
104
  \endcode
 
105
 
 
106
  \ingroup io
 
107
  \sa QPacket
 
108
*/
 
109
 
 
110
class QPacketProtocolPrivate : public QObject
 
111
{
 
112
    Q_OBJECT
 
113
public:
 
114
    QPacketProtocolPrivate(QPacketProtocol *parent, QIODevice *_dev)
 
115
        : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE),
 
116
          waitingForPacket(false), dev(_dev)
 
117
    {
 
118
        Q_ASSERT(4 == sizeof(qint32));
 
119
 
 
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)));
 
132
    }
 
133
 
 
134
Q_SIGNALS:
 
135
    void readyRead();
 
136
    void packetWritten();
 
137
    void invalidPacket();
 
138
 
 
139
public Q_SLOTS:
 
140
    void aboutToClose()
 
141
    {
 
142
        inProgress.clear();
 
143
        sendingPackets.clear();
 
144
        inProgressSize = -1;
 
145
    }
 
146
 
 
147
    void bytesWritten(qint64 bytes)
 
148
    {
 
149
        Q_ASSERT(!sendingPackets.isEmpty());
 
150
 
 
151
        while (bytes) {
 
152
            if (sendingPackets.at(0) > bytes) {
 
153
                sendingPackets[0] -= bytes;
 
154
                bytes = 0;
 
155
            } else {
 
156
                bytes -= sendingPackets.at(0);
 
157
                sendingPackets.removeFirst();
 
158
                emit packetWritten();
 
159
            }
 
160
        }
 
161
    }
 
162
 
 
163
    void readyToRead()
 
164
    {
 
165
        while (true) {
 
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())
 
170
                    return;
 
171
 
 
172
                // Read size header
 
173
                int read = dev->read((char *)&inProgressSize, sizeof(qint32));
 
174
                Q_ASSERT(read == sizeof(qint32));
 
175
                Q_UNUSED(read);
 
176
 
 
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)));
 
185
                    dev = 0;
 
186
                    emit invalidPacket();
 
187
                    return;
 
188
                }
 
189
 
 
190
                inProgressSize -= sizeof(qint32);
 
191
            } else {
 
192
                inProgress.append(dev->read(inProgressSize - inProgress.size()));
 
193
 
 
194
                if (inProgressSize == inProgress.size()) {
 
195
                    // Packet has arrived!
 
196
                    packets.append(inProgress);
 
197
                    inProgressSize = -1;
 
198
                    inProgress.clear();
 
199
 
 
200
                    waitingForPacket = false;
 
201
                    emit readyRead();
 
202
                } else
 
203
                    return;
 
204
            }
 
205
        }
 
206
    }
 
207
 
 
208
public:
 
209
    QList<qint64> sendingPackets;
 
210
    QList<QByteArray> packets;
 
211
    QByteArray inProgress;
 
212
    qint32 inProgressSize;
 
213
    qint32 maxPacketSize;
 
214
    bool waitingForPacket;
 
215
    QIODevice *dev;
 
216
};
 
217
 
 
218
/*!
 
219
  Construct a QPacketProtocol instance that works on \a dev with the
 
220
  specified \a parent.
 
221
 */
 
222
QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent)
 
223
    : QObject(parent), d(new QPacketProtocolPrivate(this, dev))
 
224
{
 
225
    Q_ASSERT(dev);
 
226
}
 
227
 
 
228
/*!
 
229
  Destroys the QPacketProtocol instance.
 
230
 */
 
231
QPacketProtocol::~QPacketProtocol()
 
232
{
 
233
}
 
234
 
 
235
/*!
 
236
  Returns the maximum packet size allowed.  By default this is
 
237
  2,147,483,647 bytes.
 
238
  
 
239
  If a packet claiming to be larger than the maximum packet size is received,
 
240
  the QPacketProtocol::invalidPacket() signal is emitted.
 
241
 
 
242
  \sa QPacketProtocol::setMaximumPacketSize()
 
243
 */
 
244
qint32 QPacketProtocol::maximumPacketSize() const
 
245
{
 
246
    return d->maxPacketSize;
 
247
}
 
248
 
 
249
/*!
 
250
  Sets the maximum allowable packet size to \a max.
 
251
 
 
252
  \sa QPacketProtocol::maximumPacketSize()
 
253
 */
 
254
qint32 QPacketProtocol::setMaximumPacketSize(qint32 max)
 
255
{
 
256
    if (max > (signed)sizeof(qint32))
 
257
        d->maxPacketSize = max;
 
258
    return d->maxPacketSize;
 
259
}
 
260
 
 
261
/*!
 
262
  Returns a streamable object that is transmitted on destruction.  For example
 
263
 
 
264
  \code
 
265
  protocol.send() << "Hello world" << 123;
 
266
  \endcode
 
267
 
 
268
  will send a packet containing "Hello world" and 123.  To construct more
 
269
  complex packets, explicitly construct a QPacket instance.
 
270
 */
 
271
QPacketAutoSend QPacketProtocol::send()
 
272
{
 
273
    return QPacketAutoSend(this);
 
274
}
 
275
 
 
276
/*!
 
277
  \fn void QPacketProtocol::send(const QPacket & packet)
 
278
 
 
279
  Transmit the \a packet.
 
280
 */
 
281
void QPacketProtocol::send(const QPacket & p)
 
282
{
 
283
    if (p.b.isEmpty())
 
284
        return; // We don't send empty packets
 
285
 
 
286
    qint64 sendSize = p.b.size() + sizeof(qint32);
 
287
 
 
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());
 
294
}
 
295
 
 
296
/*!
 
297
  Returns the number of received packets yet to be read.
 
298
  */
 
299
qint64 QPacketProtocol::packetsAvailable() const
 
300
{
 
301
    return d->packets.count();
 
302
}
 
303
 
 
304
/*!
 
305
  Discard any unread packets.
 
306
  */
 
307
void QPacketProtocol::clear()
 
308
{
 
309
    d->packets.clear();
 
310
}
 
311
 
 
312
/*!
 
313
  Return the next unread packet, or an invalid QPacket instance if no packets
 
314
  are available.  This method does NOT block.
 
315
  */
 
316
QPacket QPacketProtocol::read()
 
317
{
 
318
    if (0 == d->packets.count())
 
319
        return QPacket();
 
320
 
 
321
    QPacket rv(d->packets.at(0));
 
322
    d->packets.removeFirst();
 
323
    return rv;
 
324
}
 
325
 
 
326
/*
 
327
   Returns the difference between msecs and elapsed. If msecs is -1,
 
328
   however, -1 is returned.
 
329
*/
 
330
static int qt_timeout_value(int msecs, int elapsed)
 
331
{
 
332
    if (msecs == -1)
 
333
        return -1;
 
334
 
 
335
    int timeout = msecs - elapsed;
 
336
    return timeout < 0 ? 0 : timeout;
 
337
}
 
338
 
 
339
/*!
 
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
 
343
  30000 milliseconds.
 
344
 
 
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).
 
348
  */
 
349
 
 
350
bool QPacketProtocol::waitForReadyRead(int msecs)
 
351
{
 
352
    if (!d->packets.isEmpty())
 
353
        return true;
 
354
 
 
355
    QElapsedTimer stopWatch;
 
356
    stopWatch.start();
 
357
 
 
358
    d->waitingForPacket = true;
 
359
    do {
 
360
        if (!d->dev->waitForReadyRead(msecs))
 
361
            return false;
 
362
        if (!d->waitingForPacket)
 
363
            return true;
 
364
        msecs = qt_timeout_value(msecs, stopWatch.elapsed());
 
365
    } while (true);
 
366
}
 
367
 
 
368
/*!
 
369
  Return the QIODevice passed to the QPacketProtocol constructor.
 
370
*/
 
371
QIODevice *QPacketProtocol::device()
 
372
{
 
373
    return d->dev;
 
374
}
 
375
 
 
376
/*!
 
377
  \fn void QPacketProtocol::readyRead()
 
378
 
 
379
  Emitted whenever a new packet is received.  Applications may use
 
380
  QPacketProtocol::read() to retrieve this packet.
 
381
 */
 
382
 
 
383
/*!
 
384
  \fn void QPacketProtocol::invalidPacket()
 
385
 
 
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.
 
389
 */
 
390
 
 
391
/*!
 
392
  \fn void QPacketProtocol::packetWritten()
 
393
 
 
394
  Emitted each time a packet is completing written to the device.  This signal
 
395
  may be used for communications flow control.
 
396
 */
 
397
 
 
398
/*!
 
399
  \class QPacket
 
400
  \internal
 
401
 
 
402
  \brief The QPacket class encapsulates an unfragmentable packet of data to be
 
403
  transmitted by QPacketProtocol.
 
404
 
 
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
 
407
  and UDP.
 
408
 
 
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:
 
412
  \code
 
413
  QPacketProtocol protocol(...);
 
414
 
 
415
  QPacket myPacket;
 
416
  myPacket << "Hello world!" << 123;
 
417
  protocol.send(myPacket);
 
418
  \endcode
 
419
 
 
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.
 
423
 
 
424
  \code
 
425
  QByteArray greeting;
 
426
  int count;
 
427
 
 
428
  QPacket myPacket = protocol.read();
 
429
 
 
430
  myPacket >> greeting >> count;
 
431
  \endcode
 
432
 
 
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.
 
437
 
 
438
  \ingroup io
 
439
  \sa QPacketProtocol
 
440
 */
 
441
 
 
442
/*!
 
443
  Constructs an empty write-only packet.
 
444
  */
 
445
QPacket::QPacket()
 
446
    : QDataStream(), buf(0)
 
447
{
 
448
    buf = new QBuffer(&b);
 
449
    buf->open(QIODevice::WriteOnly);
 
450
    setDevice(buf);
 
451
    setVersion(QDataStream::Qt_4_7);
 
452
}
 
453
 
 
454
/*!
 
455
  Destroys the QPacket instance.
 
456
  */
 
457
QPacket::~QPacket()
 
458
{
 
459
    if (buf) {
 
460
        delete buf;
 
461
        buf = 0;
 
462
    }
 
463
}
 
464
 
 
465
/*!
 
466
  Creates a copy of \a other.  The initial stream positions are shared, but the
 
467
  two packets are otherwise independent.
 
468
 */
 
469
QPacket::QPacket(const QPacket & other)
 
470
    : QDataStream(), b(other.b), buf(0)
 
471
{
 
472
    buf = new QBuffer(&b);
 
473
    buf->open(other.buf->openMode());
 
474
    setDevice(buf);
 
475
}
 
476
 
 
477
/*!
 
478
  \internal
 
479
  */
 
480
QPacket::QPacket(const QByteArray & ba)
 
481
    : QDataStream(), b(ba), buf(0)
 
482
{
 
483
    buf = new QBuffer(&b);
 
484
    buf->open(QIODevice::ReadOnly);
 
485
    setDevice(buf);
 
486
}
 
487
 
 
488
/*!
 
489
  Returns true if this packet is empty - that is, contains no data.
 
490
  */
 
491
bool QPacket::isEmpty() const
 
492
{
 
493
    return b.isEmpty();
 
494
}
 
495
 
 
496
/*!
 
497
  Returns raw packet data.
 
498
  */
 
499
QByteArray QPacket::data() const
 
500
{
 
501
    return b;
 
502
}
 
503
 
 
504
/*!
 
505
  Clears data in the packet.  This is useful for reusing one writable packet.
 
506
  For example
 
507
  \code
 
508
  QPacketProtocol protocol(...);
 
509
 
 
510
  QPacket packet;
 
511
 
 
512
  packet << "Hello world!" << 123;
 
513
  protocol.send(packet);
 
514
 
 
515
  packet.clear();
 
516
  packet << "Goodbyte world!" << 789;
 
517
  protocol.send(packet);
 
518
  \endcode
 
519
 */
 
520
void QPacket::clear()
 
521
{
 
522
    QBuffer::OpenMode oldMode = buf->openMode();
 
523
    buf->close();
 
524
    b.clear();
 
525
    buf->setBuffer(&b); // reset QBuffer internals with new size of b.
 
526
    buf->open(oldMode);
 
527
}
 
528
 
 
529
/*!
 
530
  \class QPacketAutoSend
 
531
  \internal
 
532
 
 
533
  \internal
 
534
  */
 
535
QPacketAutoSend::QPacketAutoSend(QPacketProtocol *_p)
 
536
    : QPacket(), p(_p)
 
537
{
 
538
}
 
539
 
 
540
QPacketAutoSend::~QPacketAutoSend()
 
541
{
 
542
    if (!b.isEmpty())
 
543
        p->send(*this);
 
544
}
 
545
 
 
546
#include <qpacketprotocol.moc>