Qt 6.x
The Qt SDK
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#include "private/qbytearray_p.h"
87
88#ifdef QSCTPSOCKET_DEBUG
89#include <qdebug.h>
90#endif
91
93
97 : maximumChannelCount(0)
98{
99}
100
104{
105}
106
110{
111 Q_Q(QSctpSocket);
112#if defined (QSCTPSOCKET_DEBUG)
113 qDebug("QSctpSocketPrivate::canReadNotification()");
114#endif
115
116 // Handle TCP emulation mode in the base implementation.
117 if (!q->isInDatagramMode())
119
120 const int savedCurrentChannel = currentReadChannel;
121 bool currentChannelRead = false;
122 do {
123 qsizetype datagramSize = incomingDatagram.size();
125
126 do {
127 // Determine the size of the pending datagram.
128 qint64 bytesToRead = socketEngine->bytesAvailable();
129 if (bytesToRead == 0) {
130 // As a corner case, if we can't determine the size of the pending datagram,
131 // try to read 4K of data from the socket. Subsequent ::recvmsg call either
132 // fails or returns the actual length of the datagram.
133 bytesToRead = 4096;
134 }
135
136 Q_ASSERT((datagramSize + qsizetype(bytesToRead)) < MaxByteArraySize);
137 incomingDatagram.resize(datagramSize + int(bytesToRead));
138
139#if defined (QSCTPSOCKET_DEBUG)
140 qDebug("QSctpSocketPrivate::canReadNotification() about to read %lli bytes",
141 bytesToRead);
142#endif
143 qint64 readBytes = socketEngine->readDatagram(
144 incomingDatagram.data() + datagramSize, bytesToRead, &header,
146 if (readBytes <= 0) {
147 if (readBytes == -2) { // no data available for reading
148 incomingDatagram.resize(datagramSize);
149 return currentChannelRead;
150 }
151
153 if (readBytes == 0) {
155 QSctpSocket::tr("The remote host closed the connection"));
156 } else {
157#if defined (QSCTPSOCKET_DEBUG)
158 qDebug("QSctpSocketPrivate::canReadNotification() read failed: %s",
160#endif
162 }
163
164#if defined (QSCTPSOCKET_DEBUG)
165 qDebug("QSctpSocketPrivate::canReadNotification() disconnecting socket");
166#endif
167 q->disconnectFromHost();
168 return currentChannelRead;
169 }
170 datagramSize += int(readBytes); // update datagram size
171 } while (!header.endOfRecord);
172
173#if defined (QSCTPSOCKET_DEBUG)
174 qDebug("QSctpSocketPrivate::canReadNotification() got datagram from channel %i, size = %i",
175 header.streamNumber, datagramSize);
176#endif
177
178 // Drop the datagram, if opened only for writing
179 if (!q->isReadable()) {
181 continue;
182 }
183
184 // Store datagram in the channel buffer
185 Q_ASSERT(header.streamNumber < readBuffers.size());
186 incomingDatagram.resize(datagramSize);
187 readBuffers[header.streamNumber].setChunkSize(0); // set packet mode on channel buffer
190
191 if (readHeaders.size() != readBuffers.size())
193 readHeaders[header.streamNumber].push_back(header);
194
195 // Emit notifications.
196 if (header.streamNumber == savedCurrentChannel)
197 currentChannelRead = true;
198 emitReadyRead(header.streamNumber);
199
201
202 return currentChannelRead;
203}
204
208{
209 Q_Q(QSctpSocket);
210#if defined (QSCTPSOCKET_DEBUG)
211 qDebug("QSctpSocketPrivate::writeToSocket()");
212#endif
213
214 // Handle TCP emulation mode in the base implementation.
215 if (!q->isInDatagramMode())
217
218 if (!socketEngine)
219 return false;
220
221 QIpPacketHeader defaultHeader;
222 const int savedCurrentChannel = currentWriteChannel;
223 bool currentChannelWritten = false;
224 bool transmitting;
225 do {
226 transmitting = false;
227
228 for (int channel = 0; channel < writeBuffers.size(); ++channel) {
229 QRingBuffer &channelBuffer = writeBuffers[channel];
230
231 if (channelBuffer.isEmpty())
232 continue;
233
234 const bool hasHeader = (channel < writeHeaders.size())
235 && !writeHeaders[channel].empty();
236 QIpPacketHeader &header = hasHeader ? writeHeaders[channel].front() : defaultHeader;
237 header.streamNumber = channel;
238 qint64 sent = socketEngine->writeDatagram(channelBuffer.readPointer(),
239 channelBuffer.nextDataBlockSize(),
240 header);
241 if (sent < 0) {
242 if (sent == -2) // temporary error in writeDatagram
243 return currentChannelWritten;
244
246#if defined (QSCTPSOCKET_DEBUG)
247 qDebug("QSctpSocketPrivate::writeToSocket() write error, aborting. %s",
249#endif
251 // An unexpected error so close the socket.
252 q->disconnectFromHost();
253 return currentChannelWritten;
254 }
255 Q_ASSERT(sent == channelBuffer.nextDataBlockSize());
256#if defined (QSCTPSOCKET_DEBUG)
257 qDebug("QSctpSocketPrivate::writeToSocket() sent datagram of size %lli to channel %i",
258 sent, channel);
259#endif
260 transmitting = true;
261
262 // Remove datagram from the buffer
263 channelBuffer.read();
264 if (hasHeader)
266
267 // Emit notifications.
268 if (channel == savedCurrentChannel)
269 currentChannelWritten = true;
271
272 // If we were closed as a result of the bytesWritten() signal, return.
274#if defined (QSCTPSOCKET_DEBUG)
275 qDebug("QSctpSocketPrivate::writeToSocket() socket is closing - returning");
276#endif
277 return currentChannelWritten;
278 }
279 }
280 } while (transmitting);
281
282 // At this point socket is either in Connected or Closing state,
283 // write buffers are empty.
285 q->disconnectFromHost();
286 else
288
289 return currentChannelWritten;
290}
291
295{
296 if (socketEngine)
299}
300
310 : QTcpSocket(SctpSocket, *new QSctpSocketPrivate, parent)
311{
312#if defined(QSCTPSOCKET_DEBUG)
313 qDebug("QSctpSocket::QSctpSocket()");
314#endif
315 d_func()->isBuffered = true;
316}
317
324{
325#if defined(QSCTPSOCKET_DEBUG)
326 qDebug("QSctpSocket::~QSctpSocket()");
327#endif
328}
329
333{
334 Q_D(QSctpSocket);
335
336 // Cleanup headers, if the user calls the standard QIODevice functions
337 if (d->currentReadChannel < d->readHeaders.size())
338 d->readHeaders[d->currentReadChannel].clear();
339
340 return QTcpSocket::readData(data, maxSize);
341}
342
346{
347 Q_D(QSctpSocket);
348
349 // Cleanup headers, if the user calls the standard QIODevice functions
350 if (d->currentReadChannel < d->readHeaders.size())
351 d->readHeaders[d->currentReadChannel].clear();
352
353 return QTcpSocket::readLineData(data, maxlen);
354}
355
359{
361 d_func()->readHeaders.clear();
362}
363
367{
368 Q_D(QSctpSocket);
369
371 if (d->state == QAbstractSocket::UnconnectedState) {
372 d->incomingDatagram.clear();
373 d->writeHeaders.clear();
374 }
375}
376
388{
389 Q_D(QSctpSocket);
390 if (d->state != QAbstractSocket::UnconnectedState) {
391 qWarning("QSctpSocket::setMaximumChannelCount() is only allowed in UnconnectedState");
392 return;
393 }
394#if defined(QSCTPSOCKET_DEBUG)
395 qDebug("QSctpSocket::setMaximumChannelCount(%i)", count);
396#endif
397 d->maximumChannelCount = qMax(count, -1);
398}
399
413{
414 return d_func()->maximumChannelCount;
415}
416
423{
424 Q_D(const QSctpSocket);
425 return d->maximumChannelCount != -1 && d->isBuffered;
426}
427
441{
442 Q_D(QSctpSocket);
443
444 if (!isReadable() || !isInDatagramMode()) {
445 qWarning("QSctpSocket::readDatagram(): operation is not permitted");
446 return QNetworkDatagram();
447 }
448
449 if (d->currentReadChannel >= d->readHeaders.size()
450 || d->readHeaders[d->currentReadChannel].size() == 0) {
451 Q_ASSERT(d->buffer.isEmpty());
452 return QNetworkDatagram();
453 }
454
456 d->readHeaders[d->currentReadChannel].front()));
457 d->readHeaders[d->currentReadChannel].pop_front();
458
459#if defined (QSCTPSOCKET_DEBUG)
460 qDebug("QSctpSocket::readDatagram() returning datagram (%p, %i, \"%s\", %i)",
461 result.d->data.constData(),
462 result.d->data.size(),
463 result.senderAddress().toString().toLatin1().constData(),
464 result.senderPort());
465#endif
466
467 return result;
468}
469
477{
478 Q_D(QSctpSocket);
479
480 if (!isWritable() || d->state != QAbstractSocket::ConnectedState || !d->socketEngine
481 || !d->socketEngine->isValid() || !isInDatagramMode()) {
482 qWarning("QSctpSocket::writeDatagram(): operation is not permitted");
483 return false;
484 }
485
486 if (datagram.d->data.isEmpty()) {
487 qWarning("QSctpSocket::writeDatagram() is called with empty datagram");
488 return false;
489 }
490
491
492#if defined QSCTPSOCKET_DEBUG
493 qDebug("QSctpSocket::writeDatagram(%p, %i, \"%s\", %i)",
494 datagram.d->data.constData(),
495 datagram.d->data.size(),
497 datagram.destinationPort());
498#endif
499
500 if (d->writeHeaders.size() != d->writeBuffers.size())
501 d->writeHeaders.resize(d->writeBuffers.size());
502 Q_ASSERT(d->currentWriteChannel < d->writeHeaders.size());
503 d->writeHeaders[d->currentWriteChannel].push_back(datagram.d->header);
504 d->writeBuffer.setChunkSize(0); // set packet mode on channel buffer
505 d->writeBuffer.append(datagram.d->data);
506
507 d->socketEngine->setWriteNotificationEnabled(true);
508 return true;
509}
510
512
513#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.
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
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.
QString toString() const
Returns the address as a string.
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:386
void push_back(parameter_type t)
Definition qlist.h:672
reference front()
Definition qlist.h:684
void pop_front() noexcept
Definition qlist.h:677
void resize(qsizetype size)
Definition qlist.h:392
The QNetworkDatagram class provides the data and metadata of a UDP datagram.
QHostAddress destinationAddress() const
Returns the destination address associated with this datagram.
int destinationPort() const
Returns the port number of the destination associated with this datagram.
\inmodule QtCore
Definition qobject.h:90
bool isEmpty() const
const char * readPointer() const
qint64 nextDataBlockSize() const
Q_CORE_EXPORT qint64 read(char *data, qint64 maxLength)
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:559
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.
QT_BEGIN_NAMESPACE constexpr qsizetype MaxByteArraySize
static QString header(const QString &name)
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
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:70
long long qint64
Definition qtypes.h:55
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent