Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qpacketprotocol.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qpacketprotocol_p.h"
5
6#include <QtCore/QElapsedTimer>
7#include <QtCore/QtEndian>
8
9#include <private/qiodevice_p.h>
10#include <private/qobject_p.h>
11
13
68{
69 Q_DECLARE_PUBLIC(QPacketProtocol)
70public:
72
73 bool writeToDevice(const char *bytes, qint64 size);
74 bool readFromDevice(char *buffer, qint64 size);
75
76 QList<qint32> sendingPackets;
77 QList<QByteArray> packets;
82};
83
89 : QObject(*(new QPacketProtocolPrivate(dev)), parent)
90{
91 Q_ASSERT(4 == sizeof(qint32));
92 Q_ASSERT(dev);
93
94 QObject::connect(dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead);
95 QObject::connect(dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten);
96}
97
104{
105 Q_D(QPacketProtocol);
106 static const qint32 maxSize = std::numeric_limits<qint32>::max() - sizeof(qint32);
107
108 if (data.isEmpty())
109 return; // We don't send empty packets
110
111 if (data.size() > maxSize) {
112 emit error();
113 return;
114 }
115
116 const qint32 sendSize = data.size() + static_cast<qint32>(sizeof(qint32));
117 d->sendingPackets.append(sendSize);
118
119 qint32 sendSizeLE = qToLittleEndian(sendSize);
120 if (!d->writeToDevice((const char *)&sendSizeLE, sizeof(qint32))
121 || !d->writeToDevice(data.data(), data.size())) {
122 emit error();
123 }
124}
125
130{
131 Q_D(const QPacketProtocol);
132 return d->packets.size();
133}
134
140{
141 Q_D(QPacketProtocol);
142 return d->packets.isEmpty() ? QByteArray() : d->packets.takeFirst();
143}
144
157{
158 Q_D(QPacketProtocol);
159 if (!d->packets.isEmpty())
160 return true;
161
162 QElapsedTimer stopWatch;
163 stopWatch.start();
164
165 d->waitingForPacket = true;
166 do {
167 if (!d->dev->waitForReadyRead(msecs))
168 return false;
169 if (!d->waitingForPacket)
170 return true;
171 msecs = qt_subtract_from_timeout(msecs, stopWatch.elapsed());
172 } while (true);
173}
174
175void QPacketProtocol::bytesWritten(qint64 bytes)
176{
177 Q_D(QPacketProtocol);
178 Q_ASSERT(!d->sendingPackets.isEmpty());
179
180 while (bytes) {
181 if (d->sendingPackets.at(0) > bytes) {
182 d->sendingPackets[0] -= bytes;
183 bytes = 0;
184 } else {
185 bytes -= d->sendingPackets.at(0);
186 d->sendingPackets.removeFirst();
187 }
188 }
189}
190
191void QPacketProtocol::readyToRead()
192{
193 Q_D(QPacketProtocol);
194 while (true) {
195 // Need to get trailing data
196 if (-1 == d->inProgressSize) {
197 // We need a size header of sizeof(qint32)
198 if (static_cast<qint64>(sizeof(qint32)) > d->dev->bytesAvailable())
199 return;
200
201 // Read size header
202 qint32 inProgressSizeLE;
203 if (!d->readFromDevice((char *)&inProgressSizeLE, sizeof(qint32))) {
204 emit error();
205 return;
206 }
207 d->inProgressSize = qFromLittleEndian(inProgressSizeLE);
208
209 // Check sizing constraints
210 if (d->inProgressSize < qint32(sizeof(qint32))) {
211 disconnect(d->dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead);
212 disconnect(d->dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten);
213 d->dev = nullptr;
214 emit error();
215 return;
216 }
217
218 d->inProgressSize -= sizeof(qint32);
219 } else {
220
221 const int bytesToRead = static_cast<int>(
222 qMin(d->dev->bytesAvailable(),
223 static_cast<qint64>(d->inProgressSize - d->inProgress.size())));
224
225 QByteArray toRead(bytesToRead, Qt::Uninitialized);
226 if (!d->readFromDevice(toRead.data(), toRead.size())) {
227 emit error();
228 return;
229 }
230
231 d->inProgress.append(toRead);
232 if (d->inProgressSize == d->inProgress.size()) {
233 // Packet has arrived!
234 d->packets.append(d->inProgress);
235 d->inProgressSize = -1;
236 d->inProgress.clear();
237
238 d->waitingForPacket = false;
239 emit readyRead();
240 } else
241 return;
242 }
243 }
244}
245
247 inProgressSize(-1), waitingForPacket(false), dev(dev)
248{
249}
250
252{
253 qint64 totalWritten = 0;
254 while (totalWritten < size) {
255 const qint64 chunkSize = dev->write(bytes + totalWritten, size - totalWritten);
256 if (chunkSize < 0)
257 return false;
258 totalWritten += chunkSize;
259 }
260 return totalWritten == size;
261}
262
264{
265 qint64 totalRead = 0;
266 while (totalRead < size) {
267 const qint64 chunkSize = dev->read(buffer + totalRead, size - totalRead);
268 if (chunkSize < 0)
269 return false;
270 totalRead += chunkSize;
271 }
272 return totalRead == size;
273}
274
291
292#include "moc_qpacketprotocol_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
void bytesWritten(qint64 bytes)
This signal is emitted every time a payload of data has been written to the device's current write ch...
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
QList< QByteArray > packets
bool writeToDevice(const char *bytes, qint64 size)
bool readFromDevice(char *buffer, qint64 size)
QList< qint32 > sendingPackets
QPacketProtocolPrivate(QIODevice *dev)
The QPacketProtocol class encapsulates communicating discrete packets across fragmented IO channels,...
void send(const QByteArray &data)
Transmit the packet.
QPacketProtocol(QIODevice *dev, QObject *parent=nullptr)
Construct a QPacketProtocol instance that works on dev with the specified parent.
void readyRead()
Emitted whenever a new packet is received.
bool waitForReadyRead(int msecs=3000)
This function locks until a new packet is available for reading and the \l{QIODevice::}{readyRead()} ...
qint64 packetsAvailable() const
Returns the number of received packets yet to be read.
QByteArray read()
Return the next unread packet, or an empty QByteArray if no packets are available.
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
constexpr T qFromLittleEndian(T source)
Definition qendian.h:178
constexpr T qToLittleEndian(T source)
Definition qendian.h:176
int qt_subtract_from_timeout(int timeout, int elapsed)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
int qint32
Definition qtypes.h:49
long long qint64
Definition qtypes.h:60
myObject disconnect()
[26]