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
qsctpsocket.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4//#define QSCTPSOCKET_DEBUG
5
82#include "qsctpsocket.h"
83#include "qsctpsocket_p.h"
84
86
87#ifdef QSCTPSOCKET_DEBUG
88#include <qdebug.h>
89#endif
90
92
96 : maximumChannelCount(0)
97{
98}
99
105
109{
110 Q_Q(QSctpSocket);
111#if defined (QSCTPSOCKET_DEBUG)
112 qDebug("QSctpSocketPrivate::canReadNotification()");
113#endif
114
115 // Handle TCP emulation mode in the base implementation.
116 if (!q->isInDatagramMode())
118
119 const int savedCurrentChannel = currentReadChannel;
120 bool currentChannelRead = false;
121 do {
122 qsizetype datagramSize = incomingDatagram.size();
124
125 do {
126 // Determine the size of the pending datagram.
127 qint64 bytesToRead = socketEngine->bytesAvailable();
128 if (bytesToRead == 0) {
129 // As a corner case, if we can't determine the size of the pending datagram,
130 // try to read 4K of data from the socket. Subsequent ::recvmsg call either
131 // fails or returns the actual length of the datagram.
132 bytesToRead = 4096;
133 }
134
135 Q_ASSERT((datagramSize + qsizetype(bytesToRead)) < QByteArray::max_size());
136 incomingDatagram.resize(datagramSize + int(bytesToRead));
137
138#if defined (QSCTPSOCKET_DEBUG)
139 qDebug("QSctpSocketPrivate::canReadNotification() about to read %lli bytes",
140 bytesToRead);
141#endif
142 qint64 readBytes = socketEngine->readDatagram(
143 incomingDatagram.data() + datagramSize, bytesToRead, &header,
145 if (readBytes <= 0) {
146 if (readBytes == -2) { // no data available for reading
147 incomingDatagram.resize(datagramSize);
148 return currentChannelRead;
149 }
150
152 if (readBytes == 0) {
154 QSctpSocket::tr("The remote host closed the connection"));
155 } else {
156#if defined (QSCTPSOCKET_DEBUG)
157 qDebug("QSctpSocketPrivate::canReadNotification() read failed: %s",
159#endif
161 }
162
163#if defined (QSCTPSOCKET_DEBUG)
164 qDebug("QSctpSocketPrivate::canReadNotification() disconnecting socket");
165#endif
166 q->disconnectFromHost();
167 return currentChannelRead;
168 }
169 datagramSize += int(readBytes); // update datagram size
170 } while (!header.endOfRecord);
171
172#if defined (QSCTPSOCKET_DEBUG)
173 qDebug("QSctpSocketPrivate::canReadNotification() got datagram from channel %i, size = %i",
174 header.streamNumber, datagramSize);
175#endif
176
177 // Drop the datagram, if opened only for writing
178 if (!q->isReadable()) {
180 continue;
181 }
182
183 // Store datagram in the channel buffer
184 Q_ASSERT(header.streamNumber < readBuffers.size());
185 incomingDatagram.resize(datagramSize);
186 readBuffers[header.streamNumber].setChunkSize(0); // set packet mode on channel buffer
189
190 if (readHeaders.size() != readBuffers.size())
192 readHeaders[header.streamNumber].push_back(header);
193
194 // Emit notifications.
195 if (header.streamNumber == savedCurrentChannel)
196 currentChannelRead = true;
197 emitReadyRead(header.streamNumber);
198
200
201 return currentChannelRead;
202}
203
207{
208 Q_Q(QSctpSocket);
209#if defined (QSCTPSOCKET_DEBUG)
210 qDebug("QSctpSocketPrivate::writeToSocket()");
211#endif
212
213 // Handle TCP emulation mode in the base implementation.
214 if (!q->isInDatagramMode())
216
217 if (!socketEngine)
218 return false;
219
220 QIpPacketHeader defaultHeader;
221 const int savedCurrentChannel = currentWriteChannel;
222 bool currentChannelWritten = false;
223 bool transmitting;
224 do {
225 transmitting = false;
226
227 for (int channel = 0; channel < writeBuffers.size(); ++channel) {
228 QRingBuffer &channelBuffer = writeBuffers[channel];
229
230 if (channelBuffer.isEmpty())
231 continue;
232
233 const bool hasHeader = (channel < writeHeaders.size())
234 && !writeHeaders[channel].empty();
235 QIpPacketHeader &header = hasHeader ? writeHeaders[channel].front() : defaultHeader;
236 header.streamNumber = channel;
237 qint64 sent = socketEngine->writeDatagram(channelBuffer.readPointer(),
238 channelBuffer.nextDataBlockSize(),
239 header);
240 if (sent < 0) {
241 if (sent == -2) // temporary error in writeDatagram
242 return currentChannelWritten;
243
245#if defined (QSCTPSOCKET_DEBUG)
246 qDebug("QSctpSocketPrivate::writeToSocket() write error, aborting. %s",
248#endif
250 // An unexpected error so close the socket.
251 q->disconnectFromHost();
252 return currentChannelWritten;
253 }
254 Q_ASSERT(sent == channelBuffer.nextDataBlockSize());
255#if defined (QSCTPSOCKET_DEBUG)
256 qDebug("QSctpSocketPrivate::writeToSocket() sent datagram of size %lli to channel %i",
257 sent, channel);
258#endif
259 transmitting = true;
260
261 // Remove datagram from the buffer
262 channelBuffer.read();
263 if (hasHeader)
265
266 // Emit notifications.
267 if (channel == savedCurrentChannel)
268 currentChannelWritten = true;
270
271 // If we were closed as a result of the bytesWritten() signal, return.
273#if defined (QSCTPSOCKET_DEBUG)
274 qDebug("QSctpSocketPrivate::writeToSocket() socket is closing - returning");
275#endif
276 return currentChannelWritten;
277 }
278 }
279 } while (transmitting);
280
281 // At this point socket is either in Connected or Closing state,
282 // write buffers are empty.
284 q->disconnectFromHost();
285 else
287
288 return currentChannelWritten;
289}
290
299
309 : QTcpSocket(SctpSocket, *new QSctpSocketPrivate, parent)
310{
311#if defined(QSCTPSOCKET_DEBUG)
312 qDebug("QSctpSocket::QSctpSocket()");
313#endif
314 d_func()->isBuffered = true;
315}
316
323{
324#if defined(QSCTPSOCKET_DEBUG)
325 qDebug("QSctpSocket::~QSctpSocket()");
326#endif
327}
328
332{
333 Q_D(QSctpSocket);
334
335 // Cleanup headers, if the user calls the standard QIODevice functions
336 if (d->currentReadChannel < d->readHeaders.size())
337 d->readHeaders[d->currentReadChannel].clear();
338
339 return QTcpSocket::readData(data, maxSize);
340}
341
345{
346 Q_D(QSctpSocket);
347
348 // Cleanup headers, if the user calls the standard QIODevice functions
349 if (d->currentReadChannel < d->readHeaders.size())
350 d->readHeaders[d->currentReadChannel].clear();
351
352 return QTcpSocket::readLineData(data, maxlen);
353}
354
358{
360 d_func()->readHeaders.clear();
361}
362
366{
367 Q_D(QSctpSocket);
368
370 if (d->state == QAbstractSocket::UnconnectedState) {
371 d->incomingDatagram.clear();
372 d->writeHeaders.clear();
373 }
374}
375
387{
388 Q_D(QSctpSocket);
389 if (d->state != QAbstractSocket::UnconnectedState) {
390 qWarning("QSctpSocket::setMaximumChannelCount() is only allowed in UnconnectedState");
391 return;
392 }
393#if defined(QSCTPSOCKET_DEBUG)
394 qDebug("QSctpSocket::setMaximumChannelCount(%i)", count);
395#endif
396 d->maximumChannelCount = qMax(count, -1);
397}
398
412{
413 return d_func()->maximumChannelCount;
414}
415
422{
423 Q_D(const QSctpSocket);
424 return d->maximumChannelCount != -1 && d->isBuffered;
425}
426
440{
441 Q_D(QSctpSocket);
442
443 if (!isReadable() || !isInDatagramMode()) {
444 qWarning("QSctpSocket::readDatagram(): operation is not permitted");
445 return QNetworkDatagram();
446 }
447
448 if (d->currentReadChannel >= d->readHeaders.size()
449 || d->readHeaders[d->currentReadChannel].size() == 0) {
450 Q_ASSERT(d->buffer.isEmpty());
451 return QNetworkDatagram();
452 }
453
455 d->readHeaders[d->currentReadChannel].front()));
456 d->readHeaders[d->currentReadChannel].pop_front();
457
458#if defined (QSCTPSOCKET_DEBUG)
459 qDebug("QSctpSocket::readDatagram() returning datagram (%p, %i, \"%s\", %i)",
460 result.d->data.constData(),
461 result.d->data.size(),
462 result.senderAddress().toString().toLatin1().constData(),
463 result.senderPort());
464#endif
465
466 return result;
467}
468
476{
477 Q_D(QSctpSocket);
478
479 if (!isWritable() || d->state != QAbstractSocket::ConnectedState || !d->socketEngine
480 || !d->socketEngine->isValid() || !isInDatagramMode()) {
481 qWarning("QSctpSocket::writeDatagram(): operation is not permitted");
482 return false;
483 }
484
485 if (datagram.d->data.isEmpty()) {
486 qWarning("QSctpSocket::writeDatagram() is called with empty datagram");
487 return false;
488 }
489
490
491#if defined QSCTPSOCKET_DEBUG
492 qDebug("QSctpSocket::writeDatagram(%p, %i, \"%s\", %i)",
493 datagram.d->data.constData(),
494 datagram.d->data.size(),
495 datagram.destinationAddress().toString().toLatin1().constData(),
496 datagram.destinationPort());
497#endif
498
499 if (d->writeHeaders.size() != d->writeBuffers.size())
500 d->writeHeaders.resize(d->writeBuffers.size());
501 Q_ASSERT(d->currentWriteChannel < d->writeHeaders.size());
502 d->writeHeaders[d->currentWriteChannel].push_back(datagram.d->header);
503 d->writeBuffer.setChunkSize(0); // set packet mode on channel buffer
504 d->writeBuffer.append(datagram.d->data);
505
506 d->socketEngine->setWriteNotificationEnabled(true);
507 return true;
508}
509
511
512#include "moc_qsctpsocket.cpp"
IOBluetoothL2CAPChannel * channel
QAbstractSocket::SocketError error() const
virtual void setWriteNotificationEnabled(bool enable)=0
virtual qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header=nullptr, PacketHeaderOptions=WantNone)=0
virtual bool setOption(SocketOption option, int value)=0
virtual qint64 bytesAvailable() const =0
virtual void close()=0
virtual qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header)=0
void emitBytesWritten(qint64 bytes, int channel=0)
void emitReadyRead(int channel=0)
QAbstractSocketEngine * socketEngine
virtual bool canReadNotification()
QAbstractSocket::SocketState state
void setErrorAndEmit(QAbstractSocket::SocketError errorCode, const QString &errorString)
virtual void disconnectFromHost()
Attempts to close the socket.
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
static constexpr qsizetype max_size() noexcept
Definition qbytearray.h:485
void clear()
Clears the contents of the byte array and makes it null.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
QVarLengthArray< QRingBuffer, 1 > writeBuffers
QVarLengthArray< QRingBuffer, 2 > readBuffers
bool isReadable() const
Returns true if data can be read from the device; otherwise returns false.
bool isWritable() const
Returns true if data can be written to the device; otherwise returns false.
virtual qint64 readLineData(char *data, qint64 maxlen)
Reads up to maxSize characters into data and returns the number of characters read.
virtual qint64 readData(char *data, qint64 maxlen)=0
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
virtual void close()
First emits aboutToClose(), then closes the device and sets its OpenMode to NotOpen.
qsizetype size() const noexcept
Definition qlist.h:397
void push_back(parameter_type t)
Definition qlist.h:675
reference front()
Definition qlist.h:687
void pop_front() noexcept
Definition qlist.h:680
void resize(qsizetype size)
Definition qlist.h:403
The QNetworkDatagram class provides the data and metadata of a UDP datagram.
\inmodule QtCore
Definition qobject.h:103
bool canReadNotification() override
virtual ~QSctpSocketPrivate()
bool writeToSocket() override
void configureCreatedSocket() override
QByteArray incomingDatagram
QList< IpHeaderList > readHeaders
QList< IpHeaderList > writeHeaders
The QSctpSocket class provides an SCTP socket.
Definition qsctpsocket.h:17
bool writeDatagram(const QNetworkDatagram &datagram)
Writes a datagram to the buffer of the current write channel.
qint64 readData(char *data, qint64 maxlen) override
\reimp
QSctpSocket(QObject *parent=nullptr)
Creates a QSctpSocket object in state UnconnectedState.
void close() override
\reimp
void setMaximumChannelCount(int count)
Sets the maximum number of channels that the application is prepared to support in datagram mode,...
void disconnectFromHost() override
\reimp
qint64 readLineData(char *data, qint64 maxlen) override
\reimp
bool isInDatagramMode() const
Returns true if the socket is running in datagram mode.
int maximumChannelCount() const
Returns the maximum number of channels that QSctpSocket is able to support.
virtual ~QSctpSocket()
Destroys the socket, closing the connection if necessary.
QNetworkDatagram readDatagram()
Reads a datagram from the buffer of the current read channel, and returns it as a QNetworkDatagram ob...
QByteArray toLatin1() const &
Definition qstring.h:630
The QTcpSocket class provides a TCP socket.
Definition qtcpsocket.h:18
constexpr size_type size() const noexcept
void append(const T &t)
Combined button and popup list for selecting options.
static QString header(const QString &name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60