src/publishsubscribe/qpacketprotocol.cpp
changeset 0 876b1a06bc25
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 Qt Mobility Components.
       
     8 **
       
     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
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qpacketprotocol_p.h"
       
    43 #include <QBuffer>
       
    44 
       
    45 QTM_BEGIN_NAMESPACE
       
    46 
       
    47 #define MAX_PACKET_SIZE 0x7FFFFFFF
       
    48 
       
    49 /*!
       
    50   \class QPacketProtocol
       
    51   \internal
       
    52   \ingroup publishsubscribe
       
    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   \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       dev(_dev)
       
   117     {
       
   118         QObject::connect(this, SIGNAL(readyRead()),
       
   119                          parent, SIGNAL(readyRead()));
       
   120         QObject::connect(this, SIGNAL(packetWritten()),
       
   121                          parent, SIGNAL(packetWritten()));
       
   122         QObject::connect(this, SIGNAL(invalidPacket()),
       
   123                          parent, SIGNAL(invalidPacket()));
       
   124         QObject::connect(dev, SIGNAL(readyRead()),
       
   125                          this, SLOT(readyToRead()));
       
   126         QObject::connect(dev, SIGNAL(aboutToClose()),
       
   127                          this, SLOT(aboutToClose()));
       
   128         QObject::connect(dev, SIGNAL(bytesWritten(qint64)),
       
   129                          this, SLOT(bytesWritten(qint64)));
       
   130     }
       
   131 
       
   132 signals:
       
   133     void readyRead();
       
   134     void packetWritten();
       
   135     void invalidPacket();
       
   136 
       
   137 public slots:
       
   138     void aboutToClose()
       
   139     {
       
   140         inProgress.clear();
       
   141         sendingPackets.clear();
       
   142         inProgressSize = -1;
       
   143     }
       
   144 
       
   145     void bytesWritten(qint64 bytes)
       
   146     {
       
   147         Q_ASSERT(!sendingPackets.isEmpty());
       
   148 	
       
   149         while(bytes) {
       
   150             if(sendingPackets.at(0) > bytes) {
       
   151                 sendingPackets[0] -= bytes;
       
   152                 bytes = 0;
       
   153             } else {
       
   154                 bytes -= sendingPackets.at(0);
       
   155                 sendingPackets.removeFirst();
       
   156                 emit packetWritten();
       
   157             }
       
   158         }
       
   159     }
       
   160 
       
   161     void readyToRead()
       
   162     {
       
   163         if(-1 == inProgressSize) {
       
   164             // We need a size header of sizeof(qint32)
       
   165             if(sizeof(qint32) > dev->bytesAvailable())
       
   166                 return;
       
   167 
       
   168             // Read size header
       
   169             int read = dev->read((char *)&inProgressSize, sizeof(qint32));
       
   170             Q_ASSERT(read == sizeof(qint32));
       
   171             Q_UNUSED(read);
       
   172 
       
   173             // Check sizing constraints
       
   174             if(inProgressSize > maxPacketSize) {
       
   175                 QObject::disconnect(dev, SIGNAL(readyRead()),
       
   176                                     this, SLOT(readyToRead()));
       
   177                 QObject::disconnect(dev, SIGNAL(aboutToClose()),
       
   178                                     this, SLOT(aboutToClose()));
       
   179                 QObject::disconnect(dev, SIGNAL(bytesWritten(qint64)),
       
   180                                     this, SLOT(bytesWritten(qint64)));
       
   181                 dev = 0;
       
   182                 emit invalidPacket();
       
   183                 return;
       
   184             }
       
   185 
       
   186             inProgressSize -= sizeof(qint32);
       
   187 
       
   188             // Need to get trailing data
       
   189             readyToRead();
       
   190         } else {
       
   191             inProgress.append(dev->read(inProgressSize - inProgress.size()));
       
   192 
       
   193             if(inProgressSize == inProgress.size()) {
       
   194                 // Packet has arrived!
       
   195                 packets.append(inProgress);
       
   196                 inProgressSize = -1;
       
   197                 inProgress.clear();
       
   198 
       
   199                 emit readyRead();
       
   200 
       
   201                 // Need to get trailing data
       
   202                 readyToRead();
       
   203             }
       
   204         }
       
   205     }
       
   206 
       
   207 public:
       
   208     QList<qint64> sendingPackets;
       
   209     QList<QByteArray> packets;
       
   210     QByteArray inProgress;
       
   211     qint32 inProgressSize;
       
   212     qint32 maxPacketSize;
       
   213     QIODevice * dev;
       
   214 };
       
   215 
       
   216 /*!
       
   217   Construct a QPacketProtocol instance that works on \a dev with the
       
   218   specified \a parent.
       
   219  */
       
   220 QPacketProtocol::QPacketProtocol(QIODevice * dev, QObject * parent)
       
   221 : QObject(parent), d(new QPacketProtocolPrivate(this, dev))
       
   222 {
       
   223     Q_ASSERT(dev);
       
   224 }
       
   225 
       
   226 /*!
       
   227   Destroys the QPacketProtocol instance.
       
   228  */
       
   229 QPacketProtocol::~QPacketProtocol()
       
   230 {
       
   231 }
       
   232 
       
   233 /*!
       
   234   Returns the maximum packet size allowed.  By default this is
       
   235   2,147,483,647 bytes.  
       
   236   
       
   237   If a packet claiming to be larger than the maximum packet size is received,
       
   238   the QPacketProtocol::invalidPacket() signal is emitted.
       
   239 
       
   240   \sa QPacketProtocol::setMaximumPacketSize()
       
   241  */
       
   242 qint32 QPacketProtocol::maximumPacketSize() const
       
   243 {
       
   244     return d->maxPacketSize;
       
   245 }
       
   246 
       
   247 /*!
       
   248   Sets the maximum allowable packet size to \a max.
       
   249 
       
   250   \sa QPacketProtocol::maximumPacketSize()
       
   251  */
       
   252 qint32 QPacketProtocol::setMaximumPacketSize(qint32 max)
       
   253 {
       
   254     if(max > (signed)sizeof(qint32))
       
   255         d->maxPacketSize = max;
       
   256     return d->maxPacketSize;
       
   257 }
       
   258 
       
   259 /*!
       
   260   Returns a streamable object that is transmitted on destruction.  For example
       
   261 
       
   262   \code
       
   263   protocol.send() << "Hello world" << 123;
       
   264   \endcode
       
   265 
       
   266   will send a packet containing "Hello world" and 123.  To construct more 
       
   267   complex packets, explicitly construct a QPacket instance.
       
   268  */
       
   269 QPacketAutoSend QPacketProtocol::send()
       
   270 {
       
   271     return QPacketAutoSend(this);
       
   272 }
       
   273 
       
   274 /*!
       
   275   \fn void QPacketProtocol::send(const QPacket & packet)
       
   276 
       
   277   Transmit the \a packet.
       
   278  */
       
   279 void QPacketProtocol::send(const QPacket & p)
       
   280 {
       
   281     if(p.b.isEmpty())
       
   282         return; // We don't send empty packets
       
   283 
       
   284     qint64 sendSize = p.b.size() + sizeof(qint32);
       
   285 
       
   286     d->sendingPackets.append(sendSize);
       
   287     qint32 sendSize32 = sendSize;
       
   288     qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32));
       
   289     Q_ASSERT(writeBytes == sizeof(qint32));
       
   290     writeBytes = d->dev->write(p.b);
       
   291     Q_ASSERT(writeBytes == p.b.size());
       
   292 }
       
   293 
       
   294 /*!
       
   295   Returns the number of received packets yet to be read.
       
   296   */
       
   297 qint64 QPacketProtocol::packetsAvailable() const
       
   298 {
       
   299     return d->packets.count();
       
   300 }
       
   301 
       
   302 /*!
       
   303   Discard any unread packets.
       
   304   */
       
   305 void QPacketProtocol::clear()
       
   306 {
       
   307     d->packets.clear();
       
   308 }
       
   309 
       
   310 /*!
       
   311   Return the next unread packet, or an invalid QPacket instance if no packets
       
   312   are available.  This method does NOT block.
       
   313   */
       
   314 QPacket QPacketProtocol::read()
       
   315 {
       
   316     if(0 == d->packets.count())
       
   317         return QPacket();
       
   318 
       
   319     QPacket rv(d->packets.at(0));
       
   320     d->packets.removeFirst();
       
   321     return rv;
       
   322 }
       
   323 
       
   324 /*!
       
   325   Return the QIODevice passed to the QPacketProtocol constructor.
       
   326 */
       
   327 QIODevice * QPacketProtocol::device()
       
   328 {
       
   329     return d->dev;
       
   330 }
       
   331 
       
   332 /*!
       
   333   \fn void QPacketProtocol::readyRead()
       
   334 
       
   335   Emitted whenever a new packet is received.  Applications may use
       
   336   QPacketProtocol::read() to retrieve this packet.
       
   337  */
       
   338 
       
   339 /*!
       
   340   \fn void QPacketProtocol::invalidPacket()
       
   341 
       
   342   A packet larger than the maximum allowable packet size was received.  The
       
   343   packet will be discarded and, as it indicates corruption in the protocol, no
       
   344   further packets will be received.
       
   345  */
       
   346 
       
   347 /*!
       
   348   \fn void QPacketProtocol::packetWritten()
       
   349 
       
   350   Emitted each time a packet is completing written to the device.  This signal
       
   351   may be used for communications flow control.
       
   352  */
       
   353 
       
   354 /*!
       
   355   \class QPacket
       
   356   \internal
       
   357   \ingroup publishsubscribe
       
   358 
       
   359   \brief The QPacket class encapsulates an unfragmentable packet of data to be
       
   360   transmitted by QPacketProtocol.
       
   361 
       
   362   The QPacket class works together with QPacketProtocol to make it simple to
       
   363   send arbitrary sized data "packets" across fragmented transports such as TCP
       
   364   and UDP.
       
   365 
       
   366   QPacket provides a QDataStream interface to an unfragmentable packet.
       
   367   Applications should construct a QPacket, propagate it with data and then
       
   368   transmit it over a QPacketProtocol instance.  For example:
       
   369   \code
       
   370   QPacketProtocol protocol(...);
       
   371 
       
   372   QPacket myPacket;
       
   373   myPacket << "Hello world!" << 123;
       
   374   protocol.send(myPacket);
       
   375   \endcode
       
   376 
       
   377   As long as both ends of the connection are using the QPacketProtocol class,
       
   378   the data within this packet will be delivered unfragmented at the other end,
       
   379   ready for extraction.
       
   380 
       
   381   \code
       
   382   QByteArray greeting;
       
   383   int count;
       
   384 
       
   385   QPacket myPacket = protocol.read();
       
   386 
       
   387   myPacket >> greeting >> count;
       
   388   \endcode
       
   389 
       
   390   Only packets returned from QPacketProtocol::read() may be read from.  QPacket
       
   391   instances constructed by directly by applications are for transmission only 
       
   392   and are considered "write only".  Attempting to read data from them will 
       
   393   result in undefined behavior.
       
   394 
       
   395   \sa QPacketProtocol
       
   396  */
       
   397 
       
   398 /*!
       
   399   Constructs an empty write-only packet.
       
   400   */
       
   401 QPacket::QPacket()
       
   402 : QDataStream()
       
   403 {
       
   404     buf = new QBuffer(&b);
       
   405     buf->open(QIODevice::WriteOnly);
       
   406     setDevice(buf);
       
   407 }
       
   408 
       
   409 /*!
       
   410   Destroys the QPacket instance.
       
   411   */
       
   412 QPacket::~QPacket()
       
   413 {
       
   414     delete buf;
       
   415 }
       
   416 
       
   417 /*!
       
   418   Creates a copy of \a other.  The initial stream positions are shared, but the
       
   419   two packets are otherwise independent.
       
   420  */
       
   421 QPacket::QPacket(const QPacket & other)
       
   422 : QDataStream(), b(other.b)
       
   423 {
       
   424     buf = new QBuffer(&b);
       
   425     buf->open(other.buf->openMode());
       
   426     setDevice(buf);
       
   427 }
       
   428 
       
   429 /*!
       
   430     Constructs a read-only packet from \a ba.
       
   431 */
       
   432 QPacket::QPacket(const QByteArray & ba)
       
   433 : QDataStream(), b(ba)
       
   434 {
       
   435     buf = new QBuffer(&b);
       
   436     buf->open(QIODevice::ReadOnly);
       
   437     setDevice(buf);
       
   438 }
       
   439 
       
   440 /*!
       
   441   Returns true if this packet is empty - that is, contains no data.
       
   442   */
       
   443 bool QPacket::isEmpty() const
       
   444 {
       
   445     return b.isEmpty();
       
   446 }
       
   447 
       
   448 /*!
       
   449   Clears data in the packet.  This is useful for reusing one writable packet.
       
   450   For example
       
   451   \code
       
   452   QPacketProtocol protocol(...);
       
   453 
       
   454   QPacket packet;
       
   455 
       
   456   packet << "Hello world!" << 123;
       
   457   protocol.send(packet);
       
   458 
       
   459   packet.clear();
       
   460   packet << "Goodbye world!" << 789;
       
   461   protocol.send(packet);
       
   462   \endcode
       
   463  */
       
   464 void QPacket::clear()
       
   465 {
       
   466     QBuffer::OpenMode oldMode = buf->openMode();
       
   467     buf->close();
       
   468     b.clear();
       
   469     buf->setBuffer(&b); // reset QBuffer internals with new size of b.
       
   470     buf->open(oldMode);
       
   471 }
       
   472 
       
   473 QPacketAutoSend::QPacketAutoSend(QPacketProtocol * _p)
       
   474 : QPacket(), p(_p)
       
   475 {
       
   476 }
       
   477 
       
   478 QPacketAutoSend::~QPacketAutoSend()
       
   479 {
       
   480     if(!b.isEmpty())
       
   481         p->send(*this);
       
   482 }
       
   483 
       
   484 #include "moc_qpacketprotocol_p.cpp"
       
   485 #include "qpacketprotocol.moc"
       
   486 QTM_END_NAMESPACE
       
   487