|
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 |