From 75a9bd2a4f637fb8e8e3dc4609a7045547119e80 Mon Sep 17 00:00:00 2001 From: Alex Trotsenko Date: Wed, 24 Sep 2014 17:31:33 +0300 Subject: Introduce SCTP sockets support Add protocol-specific code and the QSctpServer, QSctpSocket classes. Change-Id: Ie9a1d87bd1fda866a2405043d1c15c12ded5a96e Reviewed-by: Thiago Macieira --- .../code/src_network_socket_qsctpsocket.cpp | 53 ++ src/network/kernel/qnetworkdatagram.h | 1 + src/network/kernel/qnetworkdatagram_p.h | 10 +- src/network/kernel/qnetworkproxy.cpp | 37 +- src/network/kernel/qnetworkproxy.h | 8 +- src/network/kernel/qnetworkproxy_win.cpp | 10 +- src/network/network.pro | 1 + src/network/socket/qabstractsocket.cpp | 73 ++- src/network/socket/qabstractsocket.h | 1 + src/network/socket/qabstractsocket_p.h | 7 +- src/network/socket/qabstractsocketengine_p.h | 11 +- src/network/socket/qhttpsocketengine.cpp | 22 +- src/network/socket/qhttpsocketengine_p.h | 6 +- src/network/socket/qnativesocketengine.cpp | 40 +- src/network/socket/qnativesocketengine_p.h | 6 +- src/network/socket/qnativesocketengine_unix.cpp | 182 ++++++- src/network/socket/qnativesocketengine_win.cpp | 11 + src/network/socket/qnativesocketengine_winrt.cpp | 16 + src/network/socket/qsctpserver.cpp | 250 ++++++++++ src/network/socket/qsctpserver.h | 77 +++ src/network/socket/qsctpserver_p.h | 76 +++ src/network/socket/qsctpsocket.cpp | 543 +++++++++++++++++++++ src/network/socket/qsctpsocket.h | 82 ++++ src/network/socket/qsctpsocket_p.h | 90 ++++ src/network/socket/qsocks5socketengine.cpp | 58 ++- src/network/socket/qsocks5socketengine_p.h | 6 +- src/network/socket/qtcpserver.cpp | 34 +- src/network/socket/qtcpserver.h | 3 +- src/network/socket/qtcpserver_p.h | 2 + src/network/socket/qudpsocket.cpp | 15 +- src/network/socket/socket.pri | 12 + 31 files changed, 1626 insertions(+), 117 deletions(-) create mode 100644 src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp create mode 100644 src/network/socket/qsctpserver.cpp create mode 100644 src/network/socket/qsctpserver.h create mode 100644 src/network/socket/qsctpserver_p.h create mode 100644 src/network/socket/qsctpsocket.cpp create mode 100644 src/network/socket/qsctpsocket.h create mode 100644 src/network/socket/qsctpsocket_p.h (limited to 'src/network') diff --git a/src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp b/src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp new file mode 100644 index 0000000000..3783a6f939 --- /dev/null +++ b/src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alex Trotsenko +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [0] +QSctpSocket *socket = new QSctpSocket(this); + +socket->setMaxChannelCount(16); +socket->connectToHost(QHostAddress::LocalHost, 1973); + +if (socket->waitForConnected(1000)) { + int inputChannels = socket->readChannelCount(); + int outputChannels = socket->writeChannelCount(); + + .... +} +//! [0] diff --git a/src/network/kernel/qnetworkdatagram.h b/src/network/kernel/qnetworkdatagram.h index 6cbea13afc..a20d69185a 100644 --- a/src/network/kernel/qnetworkdatagram.h +++ b/src/network/kernel/qnetworkdatagram.h @@ -104,6 +104,7 @@ public: private: QNetworkDatagramPrivate *d; friend class QUdpSocket; + friend class QSctpSocket; explicit QNetworkDatagram(QNetworkDatagramPrivate &dd); QNetworkDatagram makeReply_helper(const QByteArray &data) const; diff --git a/src/network/kernel/qnetworkdatagram_p.h b/src/network/kernel/qnetworkdatagram_p.h index e28be09508..e55651a78b 100644 --- a/src/network/kernel/qnetworkdatagram_p.h +++ b/src/network/kernel/qnetworkdatagram_p.h @@ -54,7 +54,8 @@ class QIpPacketHeader { public: QIpPacketHeader(const QHostAddress &dstAddr = QHostAddress(), quint16 port = 0) - : destinationAddress(dstAddr), ifindex(0), hopLimit(-1), destinationPort(port) + : destinationAddress(dstAddr), ifindex(0), hopLimit(-1), streamNumber(-1), + destinationPort(port), endOfRecord(false) {} void clear() @@ -63,6 +64,8 @@ public: destinationAddress.clear(); ifindex = 0; hopLimit = -1; + streamNumber = -1; + endOfRecord = false; } QHostAddress senderAddress; @@ -70,8 +73,10 @@ public: uint ifindex; int hopLimit; + int streamNumber; quint16 senderPort; quint16 destinationPort; + bool endOfRecord; }; class QNetworkDatagramPrivate @@ -81,6 +86,9 @@ public: const QHostAddress &dstAddr = QHostAddress(), quint16 port = 0) : data(data), header(dstAddr, port) {} + QNetworkDatagramPrivate(const QByteArray &data, const QIpPacketHeader &header) + : data(data), header(header) + {} QByteArray data; QIpPacketHeader header; diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp index ad78c48fd8..fc3185bd5e 100644 --- a/src/network/kernel/qnetworkproxy.cpp +++ b/src/network/kernel/qnetworkproxy.cpp @@ -212,6 +212,12 @@ lookup on a remote host name and connect to it, as opposed to requiring the application to perform the name lookup and request connection to IP addresses only. + + \value SctpTunnelingCapability Ability to open transparent, tunneled + SCTP connections to a remote host. + + \value SctpListeningCapability Ability to create a listening socket + and wait for an incoming SCTP connection from a remote host. */ #include "qnetworkproxy.h" @@ -369,7 +375,9 @@ static QNetworkProxy::Capabilities defaultCapabilitiesForType(QNetworkProxy::Pro /* [QNetworkProxy::DefaultProxy] = */ (int(QNetworkProxy::ListeningCapability) | int(QNetworkProxy::TunnelingCapability) | - int(QNetworkProxy::UdpTunnelingCapability)), + int(QNetworkProxy::UdpTunnelingCapability) | + int(QNetworkProxy::SctpTunnelingCapability) | + int(QNetworkProxy::SctpListeningCapability)), /* [QNetworkProxy::Socks5Proxy] = */ (int(QNetworkProxy::TunnelingCapability) | int(QNetworkProxy::ListeningCapability) | @@ -379,7 +387,9 @@ static QNetworkProxy::Capabilities defaultCapabilitiesForType(QNetworkProxy::Pro /* [QNetworkProxy::NoProxy] = */ (int(QNetworkProxy::ListeningCapability) | int(QNetworkProxy::TunnelingCapability) | - int(QNetworkProxy::UdpTunnelingCapability)), + int(QNetworkProxy::UdpTunnelingCapability) | + int(QNetworkProxy::SctpTunnelingCapability) | + int(QNetworkProxy::SctpListeningCapability)), /* [QNetworkProxy::HttpProxy] = */ (int(QNetworkProxy::TunnelingCapability) | int(QNetworkProxy::CachingCapability) | @@ -965,6 +975,14 @@ template<> void QSharedDataPointer::detach() can all be used or be left unused, depending on the characteristics of the socket. The URL component is not used. + \row + \li SctpSocket + \li Message-oriented sockets requesting a connection to a remote + server. The peer hostname and peer port match the values passed + to QSctpSocket::connectToHost(). The local port is usually -1, + indicating the socket has no preference in which port should be + used. The URL component is not used. + \row \li TcpServer \li Passive server sockets that listen on a port and await @@ -981,6 +999,14 @@ template<> void QSharedDataPointer::detach() indicate that more detailed information is present in the URL component. For ease of implementation, the URL's host and port are set as the destination address. + + \row + \li SctpServer + \li Passive server sockets that listen on a SCTP port and await + incoming connections from the network. Normally, only the + local port is used, but the remote address could be used in + specific circumstances, for example to indicate which remote + host a connection is expected from. The URL component is not used. \endtable It should be noted that any of the criteria may be missing or @@ -1001,10 +1027,13 @@ template<> void QSharedDataPointer::detach() \value TcpSocket a normal, outgoing TCP socket \value UdpSocket a datagram-based UDP socket, which could send to multiple destinations + \value SctpSocket a message-oriented, outgoing SCTP socket \value TcpServer a TCP server that listens for incoming connections from the network \value UrlRequest a more complex request which involves loading of a URL + \value SctpServer a SCTP server that listens for incoming + connections from the network \sa queryType(), setQueryType() */ @@ -1614,6 +1643,10 @@ QDebug operator<<(QDebug debug, const QNetworkProxy &proxy) scaps << QStringLiteral("Caching"); if (caps & QNetworkProxy::HostNameLookupCapability) scaps << QStringLiteral("NameLookup"); + if (caps & QNetworkProxy::SctpTunnelingCapability) + scaps << QStringLiteral("SctpTunnel"); + if (caps & QNetworkProxy::SctpListeningCapability) + scaps << QStringLiteral("SctpListen"); debug << '[' << scaps.join(QLatin1Char(' ')) << ']'; return debug; } diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h index c9f4372596..fc919a24a6 100644 --- a/src/network/kernel/qnetworkproxy.h +++ b/src/network/kernel/qnetworkproxy.h @@ -60,8 +60,10 @@ public: enum QueryType { TcpSocket, UdpSocket, + SctpSocket, TcpServer = 100, - UrlRequest + UrlRequest, + SctpServer }; QNetworkProxyQuery(); @@ -141,7 +143,9 @@ public: ListeningCapability = 0x0002, UdpTunnelingCapability = 0x0004, CachingCapability = 0x0008, - HostNameLookupCapability = 0x0010 + HostNameLookupCapability = 0x0010, + SctpTunnelingCapability = 0x00020, + SctpListeningCapability = 0x00040 }; Q_DECLARE_FLAGS(Capabilities, Capability) diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp index 2727bd9257..c022c718cf 100644 --- a/src/network/kernel/qnetworkproxy_win.cpp +++ b/src/network/kernel/qnetworkproxy_win.cpp @@ -232,9 +232,15 @@ static QList filterProxyListByCapabilities(const QList parseServerList(const QNetworkProxyQuery &query, con QList result; QHash taggedProxies; const QString requiredTag = query.protocolTag(); - bool checkTags = !requiredTag.isEmpty() && query.queryType() != QNetworkProxyQuery::TcpServer; //windows tags are only for clients + // windows tags are only for clients + bool checkTags = !requiredTag.isEmpty() && query.queryType() != QNetworkProxyQuery::TcpServer + && query.queryType() != QNetworkProxyQuery::SctpServer; for (const QString &entry : proxyList) { int server = 0; diff --git a/src/network/network.pro b/src/network/network.pro index 256d718df6..75105bd681 100644 --- a/src/network/network.pro +++ b/src/network/network.pro @@ -9,6 +9,7 @@ DEFINES += QT_NO_USING_NAMESPACE QT_NO_FOREACH #DEFINES += QABSTRACTSOCKET_DEBUG QNATIVESOCKETENGINE_DEBUG #DEFINES += QTCPSOCKETENGINE_DEBUG QTCPSOCKET_DEBUG QTCPSERVER_DEBUG QSSLSOCKET_DEBUG #DEFINES += QUDPSOCKET_DEBUG QUDPSERVER_DEBUG +#DEFINES += QSCTPSOCKET_DEBUG QSCTPSERVER_DEBUG win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x64000000 QMAKE_DOCS = $$PWD/doc/qtnetwork.qdocconf diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index cbae297278..0615cd1cb3 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -267,7 +267,8 @@ \value TcpSocket TCP \value UdpSocket UDP - \value UnknownSocketType Other than TCP and UDP + \value SctpSocket SCTP + \value UnknownSocketType Other than TCP, UDP and SCTP \sa QAbstractSocket::socketType() */ @@ -626,6 +627,7 @@ bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtoc QString typeStr; if (q->socketType() == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket"); else if (q->socketType() == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket"); + else if (q->socketType() == QAbstractSocket::SctpSocket) typeStr = QLatin1String("SctpSocket"); else typeStr = QLatin1String("UnknownSocketType"); QString protocolStr; if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol"); @@ -670,6 +672,12 @@ bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtoc */ void QAbstractSocketPrivate::configureCreatedSocket() { +#ifndef QT_NO_SCTP + Q_Q(QAbstractSocket); + // Set single stream mode for unbuffered SCTP socket + if (socketEngine && q->socketType() == QAbstractSocket::SctpSocket) + socketEngine->setOption(QAbstractSocketEngine::MaxStreamsSocketOption, 1); +#endif } /*! \internal @@ -771,7 +779,8 @@ void QAbstractSocketPrivate::canCloseNotification() QMetaObject::invokeMethod(socketEngine, "closeNotification", Qt::QueuedConnection); } - } else if (socketType == QAbstractSocket::TcpSocket && socketEngine) { + } else if ((socketType == QAbstractSocket::TcpSocket || + socketType == QAbstractSocket::SctpSocket) && socketEngine) { emitReadyRead(); } } @@ -862,13 +871,9 @@ bool QAbstractSocketPrivate::writeToSocket() if (written > 0) { // Remove what we wrote so far. writeBuffer.free(written); - // Don't emit bytesWritten() recursively. - if (!emittedBytesWritten) { - QScopedValueRollback r(emittedBytesWritten); - emittedBytesWritten = true; - emit q->bytesWritten(written); - } - emit q->channelBytesWritten(0, written); + + // Emit notifications. + emitBytesWritten(written); } if (writeBuffer.isEmpty() && socketEngine && !socketEngine->bytesToWrite()) @@ -889,7 +894,7 @@ bool QAbstractSocketPrivate::flush() { bool dataWasWritten = false; - while (!writeBuffer.isEmpty() && writeToSocket()) + while (!allWriteBuffersEmpty() && writeToSocket()) dataWasWritten = true; return dataWasWritten; @@ -912,6 +917,8 @@ void QAbstractSocketPrivate::resolveProxy(const QString &hostname, quint16 port) QNetworkProxyQuery query(hostname, port, QString(), socketType == QAbstractSocket::TcpSocket ? QNetworkProxyQuery::TcpSocket : + socketType == QAbstractSocket::SctpSocket ? + QNetworkProxyQuery::SctpSocket : QNetworkProxyQuery::UdpSocket); proxies = QNetworkProxyFactory::proxyForQuery(query); } @@ -926,6 +933,10 @@ void QAbstractSocketPrivate::resolveProxy(const QString &hostname, quint16 port) (p.capabilities() & QNetworkProxy::TunnelingCapability) == 0) continue; + if (socketType == QAbstractSocket::SctpSocket && + (p.capabilities() & QNetworkProxy::SctpTunnelingCapability) == 0) + continue; + proxyInUse = p; return; } @@ -1280,16 +1291,34 @@ bool QAbstractSocketPrivate::readFromSocket() Prevents from the recursive readyRead() emission. */ -void QAbstractSocketPrivate::emitReadyRead() +void QAbstractSocketPrivate::emitReadyRead(int channel) { Q_Q(QAbstractSocket); // Only emit readyRead() when not recursing. - if (!emittedReadyRead) { + if (!emittedReadyRead && channel == currentReadChannel) { QScopedValueRollback r(emittedReadyRead); emittedReadyRead = true; emit q->readyRead(); } - emit q->channelReadyRead(0); + // channelReadyRead() can be emitted recursively - even for the same channel. + emit q->channelReadyRead(channel); +} + +/*! \internal + + Prevents from the recursive bytesWritten() emission. +*/ +void QAbstractSocketPrivate::emitBytesWritten(qint64 bytes, int channel) +{ + Q_Q(QAbstractSocket); + // Only emit bytesWritten() when not recursing. + if (!emittedBytesWritten && channel == currentWriteChannel) { + QScopedValueRollback r(emittedBytesWritten); + emittedBytesWritten = true; + emit q->bytesWritten(bytes); + } + // channelBytesWritten() can be emitted recursively - even for the same channel. + emit q->channelBytesWritten(channel, bytes); } /*! \internal @@ -1400,8 +1429,8 @@ QAbstractSocket::QAbstractSocket(SocketType socketType, Q_D(QAbstractSocket); #if defined(QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocket::QAbstractSocket(%sSocket, QAbstractSocketPrivate == %p, parent == %p)", - socketType == TcpSocket ? "Tcp" : socketType == UdpSocket - ? "Udp" : "Unknown", &dd, parent); + socketType == TcpSocket ? "Tcp" : socketType == UdpSocket ? "Udp" + : socketType == SctpSocket ? "Sctp" : "Unknown", &dd, parent); #endif d->socketType = socketType; } @@ -1665,9 +1694,9 @@ void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, #endif if (openMode & QIODevice::Unbuffered) - d->isBuffered = false; // Unbuffered QTcpSocket + d->isBuffered = false; else if (!d_func()->isBuffered) - openMode |= QAbstractSocket::Unbuffered; // QUdpSocket + openMode |= QAbstractSocket::Unbuffered; QIODevice::open(openMode); d->readChannelCount = d->writeChannelCount = 0; @@ -2503,10 +2532,8 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size) qt_prettyDebug(data, qMin((int)size, 32), size).data(), size, written); #endif - if (written >= 0) { - emit bytesWritten(written); - emit channelBytesWritten(0, written); - } + if (written >= 0) + d->emitBytesWritten(written); return written; } @@ -2714,14 +2741,14 @@ void QAbstractSocket::disconnectFromHost() } // Wait for pending data to be written. - if (d->socketEngine && d->socketEngine->isValid() && (d->writeBuffer.size() > 0 + if (d->socketEngine && d->socketEngine->isValid() && (!d->allWriteBuffersEmpty() || d->socketEngine->bytesToWrite() > 0)) { // hack: when we are waiting for the socket engine to write bytes (only // possible when using Socks5 or HTTP socket engine), then close // anyway after 2 seconds. This is to prevent a timeout on Mac, where we // sometimes just did not get the write notifier from the underlying // CFSocket and no progress was made. - if (d->writeBuffer.size() == 0 && d->socketEngine->bytesToWrite() > 0) { + if (d->allWriteBuffersEmpty() && d->socketEngine->bytesToWrite() > 0) { if (!d->disconnectTimer) { d->disconnectTimer = new QTimer(this); connect(d->disconnectTimer, SIGNAL(timeout()), this, diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index 2c32fa046f..73a8f11537 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -64,6 +64,7 @@ public: enum SocketType { TcpSocket, UdpSocket, + SctpSocket, UnknownSocketType = -1 }; Q_ENUM(SocketType) diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h index 41a8cf1c6b..1578d7bb35 100644 --- a/src/network/socket/qabstractsocket_p.h +++ b/src/network/socket/qabstractsocket_p.h @@ -86,7 +86,7 @@ public: virtual bool bind(const QHostAddress &address, quint16 port, QAbstractSocket::BindMode mode); - bool canReadNotification(); + virtual bool canReadNotification(); bool canWriteNotification(); void canCloseNotification(); @@ -136,8 +136,9 @@ public: void startConnectingByName(const QString &host); void fetchConnectionParameters(); bool readFromSocket(); - bool writeToSocket(); - void emitReadyRead(); + virtual bool writeToSocket(); + void emitReadyRead(int channel = 0); + void emitBytesWritten(qint64 bytes, int channel = 0); void setError(QAbstractSocket::SocketError errorCode, const QString &errorString); void setErrorAndEmit(QAbstractSocket::SocketError errorCode, const QString &errorString); diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index 4b835ce6ef..0cb519ce90 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -104,7 +104,8 @@ public: MulticastLoopbackOption, TypeOfServiceOption, ReceivePacketInformation, - ReceiveHopLimit + ReceiveHopLimit, + MaxStreamsSocketOption }; enum PacketHeaderOption { @@ -112,6 +113,8 @@ public: WantDatagramSender = 0x01, WantDatagramDestination = 0x02, WantDatagramHopLimit = 0x04, + WantStreamNumber = 0x08, + WantEndOfRecord = 0x10, WantAll = 0xff }; @@ -147,13 +150,13 @@ public: virtual bool setMulticastInterface(const QNetworkInterface &iface) = 0; #endif // QT_NO_NETWORKINTERFACE - virtual qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header = 0, - PacketHeaderOptions = WantNone) = 0; - virtual qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) = 0; virtual bool hasPendingDatagrams() const = 0; virtual qint64 pendingDatagramSize() const = 0; #endif // QT_NO_UDPSOCKET + virtual qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header = 0, + PacketHeaderOptions = WantNone) = 0; + virtual qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) = 0; virtual qint64 bytesToWrite() const = 0; virtual int option(SocketOption option) const = 0; diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index f9ff958525..899c02fba6 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -289,34 +289,34 @@ bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &) } #endif // QT_NO_NETWORKINTERFACE -qint64 QHttpSocketEngine::readDatagram(char *, qint64, QIpPacketHeader *, PacketHeaderOptions) +bool QHttpSocketEngine::hasPendingDatagrams() const { qWarning("Operation is not supported"); - setError(QAbstractSocket::UnsupportedSocketOperationError, - QLatin1String("Unsupported socket operation")); - return -1; + return false; } -qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QIpPacketHeader &) +qint64 QHttpSocketEngine::pendingDatagramSize() const { qWarning("Operation is not supported"); - setError(QAbstractSocket::UnsupportedSocketOperationError, - QLatin1String("Unsupported socket operation")); return -1; } +#endif // QT_NO_UDPSOCKET -bool QHttpSocketEngine::hasPendingDatagrams() const +qint64 QHttpSocketEngine::readDatagram(char *, qint64, QIpPacketHeader *, PacketHeaderOptions) { qWarning("Operation is not supported"); - return false; + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Unsupported socket operation")); + return -1; } -qint64 QHttpSocketEngine::pendingDatagramSize() const +qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QIpPacketHeader &) { qWarning("Operation is not supported"); + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Unsupported socket operation")); return -1; } -#endif // QT_NO_UDPSOCKET qint64 QHttpSocketEngine::bytesToWrite() const { diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h index 87400812a7..07815a7e51 100644 --- a/src/network/socket/qhttpsocketengine_p.h +++ b/src/network/socket/qhttpsocketengine_p.h @@ -112,13 +112,13 @@ public: bool setMulticastInterface(const QNetworkInterface &iface) Q_DECL_OVERRIDE; #endif // QT_NO_NETWORKINTERFACE - qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *, - PacketHeaderOptions) Q_DECL_OVERRIDE; - qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE; bool hasPendingDatagrams() const Q_DECL_OVERRIDE; qint64 pendingDatagramSize() const Q_DECL_OVERRIDE; #endif // QT_NO_UDPSOCKET + qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *, + PacketHeaderOptions) Q_DECL_OVERRIDE; + qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE; qint64 bytesToWrite() const Q_DECL_OVERRIDE; int option(SocketOption option) const Q_DECL_OVERRIDE; diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index 7c70d664e7..f2bc3cec94 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -174,6 +174,12 @@ QT_BEGIN_NAMESPACE " socket other than "#type""); \ return (returnValue); \ } } while (0) +#define Q_CHECK_TYPES(function, type1, type2, returnValue) do { \ + if (d->socketType != (type1) && d->socketType != (type2)) { \ + qWarning(#function" was called by a" \ + " socket other than "#type1" or "#type2); \ + return (returnValue); \ + } } while (0) #define Q_TR(a) QT_TRANSLATE_NOOP(QNativeSocketEngine, a) /*! \internal @@ -417,6 +423,7 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb QString typeStr = QLatin1String("UnknownSocketType"); if (socketType == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket"); else if (socketType == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket"); + else if (socketType == QAbstractSocket::SctpSocket) typeStr = QLatin1String("SctpSocket"); QString protocolStr = QLatin1String("UnknownProtocol"); if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol"); else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = QLatin1String("IPv6Protocol"); @@ -659,7 +666,12 @@ bool QNativeSocketEngine::listen() Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false); Q_CHECK_STATE(QNativeSocketEngine::listen(), QAbstractSocket::BoundState, false); +#ifndef QT_NO_SCTP + Q_CHECK_TYPES(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, + QAbstractSocket::SctpSocket, false); +#else Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false); +#endif // We're using a backlog of 50. Most modern kernels support TCP // syncookies by default, and if they do, the backlog is ignored. @@ -680,7 +692,12 @@ int QNativeSocketEngine::accept() Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1); Q_CHECK_STATE(QNativeSocketEngine::accept(), QAbstractSocket::ListeningState, -1); +#ifndef QT_NO_SCTP + Q_CHECK_TYPES(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, + QAbstractSocket::SctpSocket, -1); +#else Q_CHECK_TYPE(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, -1); +#endif return d->nativeAccept(); } @@ -793,6 +810,7 @@ qint64 QNativeSocketEngine::pendingDatagramSize() const return d->nativePendingDatagramSize(); } +#endif // QT_NO_UDPSOCKET /*! Reads up to \a maxSize bytes of a datagram from the socket, @@ -800,9 +818,10 @@ qint64 QNativeSocketEngine::pendingDatagramSize() const address, port, and other IP header fields are stored in \a header according to the request in \a options. - To avoid unnecessarily loss of data, call pendingDatagramSize() to - determine the size of the pending message before reading it. If \a - maxSize is too small, the rest of the datagram will be lost. + For UDP sockets, to avoid unnecessarily loss of data, call + pendingDatagramSize() to determine the size of the pending message + before reading it. If \a maxSize is too small, the rest of the + datagram will be lost. Returns -1 if an error occurred. @@ -813,13 +832,14 @@ qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxSize, QIpPacketHe { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::readDatagram(), -1); - Q_CHECK_TYPE(QNativeSocketEngine::readDatagram(), QAbstractSocket::UdpSocket, -1); + Q_CHECK_STATES(QNativeSocketEngine::readDatagram(), QAbstractSocket::BoundState, + QAbstractSocket::ConnectedState, -1); return d->nativeReceiveDatagram(data, maxSize, header, options); } /*! - Writes a UDP datagram of size \a size bytes to the socket from + Writes a datagram of size \a size bytes to the socket from \a data to the destination contained in \a header, and returns the number of bytes written, or -1 if an error occurred. If \a header contains other settings like hop limit or source address, this function @@ -844,11 +864,11 @@ qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 size, const Q { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::writeDatagram(), -1); - Q_CHECK_TYPE(QNativeSocketEngine::writeDatagram(), QAbstractSocket::UdpSocket, -1); + Q_CHECK_STATES(QNativeSocketEngine::writeDatagram(), QAbstractSocket::BoundState, + QAbstractSocket::ConnectedState, -1); return d->nativeSendDatagram(data, size, header); } -#endif // QT_NO_UDPSOCKET /*! Writes a block of \a size bytes from \a data to the socket. @@ -881,7 +901,11 @@ qint64 QNativeSocketEngine::read(char *data, qint64 maxSize) qint64 readBytes = d->nativeRead(data, maxSize); // Handle remote close - if (readBytes == 0 && d->socketType == QAbstractSocket::TcpSocket) { + if (readBytes == 0 && (d->socketType == QAbstractSocket::TcpSocket +#ifndef QT_NO_SCTP + || d->socketType == QAbstractSocket::SctpSocket +#endif + )) { d->setError(QAbstractSocket::RemoteHostClosedError, QNativeSocketEnginePrivate::RemoteHostClosedErrorString); close(); diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 8b1a272006..1ca0fa0213 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -157,13 +157,13 @@ public: bool setMulticastInterface(const QNetworkInterface &iface) Q_DECL_OVERRIDE; #endif - qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0, - PacketHeaderOptions = WantNone) Q_DECL_OVERRIDE; - qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE; bool hasPendingDatagrams() const Q_DECL_OVERRIDE; qint64 pendingDatagramSize() const Q_DECL_OVERRIDE; #endif // QT_NO_UDPSOCKET + qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0, + PacketHeaderOptions = WantNone) Q_DECL_OVERRIDE; + qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE; qint64 bytesToWrite() const Q_DECL_OVERRIDE; qint64 receiveBufferSize() const; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index fcb8a60c8f..2a9d600630 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -68,6 +68,11 @@ #endif #include +#ifndef QT_NO_SCTP +#include +#include +#include +#endif QT_BEGIN_NAMESPACE @@ -142,6 +147,7 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, switch (opt) { case QNativeSocketEngine::NonBlockingSocketOption: // fcntl, not setsockopt case QNativeSocketEngine::BindExclusively: // not handled on Unix + case QNativeSocketEngine::MaxStreamsSocketOption: Q_UNREACHABLE(); case QNativeSocketEngine::BroadcastSocketOption: @@ -229,13 +235,28 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &socketProtocol) { - int protocol = (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) ? AF_INET6 : AF_INET; +#ifndef QT_NO_SCTP + int protocol = (socketType == QAbstractSocket::SctpSocket) ? IPPROTO_SCTP : 0; +#else + if (socketType == QAbstractSocket::SctpSocket) { + setError(QAbstractSocket::UnsupportedSocketOperationError, + ProtocolUnsupportedErrorString); +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QNativeSocketEnginePrivate::createNewSocket(%d, %d): unsupported protocol", + socketType, socketProtocol); +#endif + return false; + } + int protocol = 0; +#endif // QT_NO_SCTP + int domain = (socketProtocol == QAbstractSocket::IPv6Protocol + || socketProtocol == QAbstractSocket::AnyIPProtocol) ? AF_INET6 : AF_INET; int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM; - int socket = qt_safe_socket(protocol, type, 0, O_NONBLOCK); + int socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK); if (socket < 0 && socketProtocol == QAbstractSocket::AnyIPProtocol && errno == EAFNOSUPPORT) { - protocol = AF_INET; - socket = qt_safe_socket(protocol, type, 0, O_NONBLOCK); + domain = AF_INET; + socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK); socketProtocol = QAbstractSocket::IPv4Protocol; } @@ -291,10 +312,26 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co if (!q->isValid()) return -1; - // handle non-getsockopt cases first - if (opt == QNativeSocketEngine::BindExclusively || opt == QNativeSocketEngine::NonBlockingSocketOption - || opt == QNativeSocketEngine::BroadcastSocketOption) + // handle non-getsockopt and specific cases first + switch (opt) { + case QNativeSocketEngine::BindExclusively: + case QNativeSocketEngine::NonBlockingSocketOption: + case QNativeSocketEngine::BroadcastSocketOption: return true; + case QNativeSocketEngine::MaxStreamsSocketOption: { +#ifndef QT_NO_SCTP + sctp_initmsg sctpInitMsg; + QT_SOCKOPTLEN_T sctpInitMsgSize = sizeof(sctpInitMsg); + if (::getsockopt(socketDescriptor, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg, + &sctpInitMsgSize) == 0) + return int(qMin(sctpInitMsg.sinit_num_ostreams, sctpInitMsg.sinit_max_instreams)); +#endif + return -1; + } + + default: + break; + } int n, level; int v = -1; @@ -317,7 +354,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt if (!q->isValid()) return false; - // handle non-setsockopt cases first + // handle non-setsockopt and specific cases first switch (opt) { case QNativeSocketEngine::NonBlockingSocketOption: { // Make the socket nonblocking. @@ -351,6 +388,20 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::BindExclusively: return true; + case QNativeSocketEngine::MaxStreamsSocketOption: { +#ifndef QT_NO_SCTP + sctp_initmsg sctpInitMsg; + QT_SOCKOPTLEN_T sctpInitMsgSize = sizeof(sctpInitMsg); + if (::getsockopt(socketDescriptor, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg, + &sctpInitMsgSize) == 0) { + sctpInitMsg.sinit_num_ostreams = sctpInitMsg.sinit_max_instreams = uint16_t(v); + return ::setsockopt(socketDescriptor, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg, + sctpInitMsgSize) == 0; + } +#endif + return false; + } + default: break; } @@ -829,6 +880,9 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) #if !defined(IP_PKTINFO) && defined(IP_RECVIF) && defined(Q_OS_BSD4) + CMSG_SPACE(sizeof(sockaddr_dl)) +#endif +#ifndef QT_NO_SCTP + + CMSG_SPACE(sizeof(struct sctp_sndrcvinfo)) #endif + sizeof(quintptr) - 1) / sizeof(quintptr)]; @@ -848,7 +902,8 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS msg.msg_name = &aa; msg.msg_namelen = sizeof(aa); } - if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination)) { + if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination + | QAbstractSocketEngine::WantStreamNumber)) { msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); } @@ -859,13 +914,27 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS } while (recvResult == -1 && errno == EINTR); if (recvResult == -1) { - setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString); + switch (errno) { +#if EWOULDBLOCK-0 && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + case EAGAIN: + // No datagram was available for reading + recvResult = -2; + break; + case ECONNREFUSED: + setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString); + break; + default: + setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString); + } if (header) header->clear(); } else if (options != QAbstractSocketEngine::WantNone) { Q_ASSERT(header); qt_socket_getPortAndAddress(&aa, &header->senderPort, &header->senderAddress); header->destinationPort = localPort; + header->endOfRecord = (msg.msg_flags & MSG_EOR) != 0; // parse the ancillary data struct cmsghdr *cmsgptr; @@ -912,6 +981,15 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS || (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_TTL))) { header->hopLimit = *reinterpret_cast(CMSG_DATA(cmsgptr)); } + +#ifndef QT_NO_SCTP + if (cmsgptr->cmsg_level == IPPROTO_SCTP && cmsgptr->cmsg_type == SCTP_SNDRCV + && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(sctp_sndrcvinfo))) { + sctp_sndrcvinfo *rcvInfo = reinterpret_cast(CMSG_DATA(cmsgptr)); + + header->streamNumber = int(rcvInfo->sinfo_stream); + } +#endif } } @@ -924,13 +1002,17 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS ? header->senderPort : 0, (qint64) recvResult); #endif - return qint64(maxSize ? recvResult : recvResult == -1 ? -1 : 0); + return qint64((maxSize || recvResult < 0) ? recvResult : Q_INT64_C(0)); } qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len, const QIpPacketHeader &header) { // we use quintptr to force the alignment - quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) + sizeof(quintptr) - 1) / sizeof(quintptr)]; + quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) +#ifndef QT_NO_SCTP + + CMSG_SPACE(sizeof(struct sctp_sndrcvinfo)) +#endif + + sizeof(quintptr) - 1) / sizeof(quintptr)]; struct cmsghdr *cmsgptr = reinterpret_cast(cbuf); struct msghdr msg; @@ -943,10 +1025,13 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l vec.iov_len = len; msg.msg_iov = &vec; msg.msg_iovlen = 1; - msg.msg_name = &aa.a; msg.msg_control = &cbuf; - setPortAndAddress(header.destinationPort, header.destinationAddress, &aa, &msg.msg_namelen); + if (header.destinationPort != 0) { + msg.msg_name = &aa.a; + setPortAndAddress(header.destinationPort, header.destinationAddress, + &aa, &msg.msg_namelen); + } if (msg.msg_namelen == sizeof(aa.a6)) { if (header.hopLimit != -1) { @@ -1001,15 +1086,37 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l #endif } +#ifndef QT_NO_SCTP + if (header.streamNumber != -1) { + struct sctp_sndrcvinfo *data = reinterpret_cast(CMSG_DATA(cmsgptr)); + memset(data, 0, sizeof(*data)); + msg.msg_controllen += CMSG_SPACE(sizeof(sctp_sndrcvinfo)); + cmsgptr->cmsg_len = CMSG_LEN(sizeof(sctp_sndrcvinfo)); + cmsgptr->cmsg_level = IPPROTO_SCTP; + cmsgptr->cmsg_type = SCTP_SNDRCV; + data->sinfo_stream = uint16_t(header.streamNumber); + cmsgptr = reinterpret_cast(reinterpret_cast(cmsgptr) + CMSG_SPACE(sizeof(*data))); + } +#endif + if (msg.msg_controllen == 0) msg.msg_control = 0; ssize_t sentBytes = qt_safe_sendmsg(socketDescriptor, &msg, 0); if (sentBytes < 0) { switch (errno) { +#if EWOULDBLOCK-0 && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + case EAGAIN: + sentBytes = -2; + break; case EMSGSIZE: setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString); break; + case ECONNRESET: + setError(QAbstractSocket::RemoteHostClosedError, RemoteHostClosedErrorString); + break; default: setError(QAbstractSocket::NetworkError, SendDatagramErrorString); } @@ -1082,21 +1189,51 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters() #endif // Determine the remote address - if (!::getpeername(socketDescriptor, &sa.a, &sockAddrSize)) { + bool connected = ::getpeername(socketDescriptor, &sa.a, &sockAddrSize) == 0; + if (connected) { qt_socket_getPortAndAddress(&sa, &peerPort, &peerAddress); inboundStreamCount = outboundStreamCount = 1; } - // Determine the socket type (UDP/TCP) + // Determine the socket type (UDP/TCP/SCTP) int value = 0; QT_SOCKOPTLEN_T valueSize = sizeof(int); if (::getsockopt(socketDescriptor, SOL_SOCKET, SO_TYPE, &value, &valueSize) == 0) { - if (value == SOCK_STREAM) - socketType = QAbstractSocket::TcpSocket; - else if (value == SOCK_DGRAM) - socketType = QAbstractSocket::UdpSocket; - else - socketType = QAbstractSocket::UnknownSocketType; + if (value == SOCK_STREAM) { +#ifndef QT_NO_SCTP + if (option(QNativeSocketEngine::MaxStreamsSocketOption) != -1) { + socketType = QAbstractSocket::SctpSocket; + if (connected) { + sctp_status sctpStatus; + QT_SOCKOPTLEN_T sctpStatusSize = sizeof(sctpStatus); + sctp_event_subscribe sctpEvents; + + memset(&sctpEvents, 0, sizeof(sctpEvents)); + sctpEvents.sctp_data_io_event = 1; + if (::getsockopt(socketDescriptor, SOL_SCTP, SCTP_STATUS, &sctpStatus, + &sctpStatusSize) == 0 && + ::setsockopt(socketDescriptor, SOL_SCTP, SCTP_EVENTS, &sctpEvents, + sizeof(sctpEvents)) == 0) { + inboundStreamCount = int(sctpStatus.sstat_instrms); + outboundStreamCount = int(sctpStatus.sstat_outstrms); + } else { + setError(QAbstractSocket::UnsupportedSocketOperationError, + InvalidSocketErrorString); + return false; + } + } + } else { + socketType = QAbstractSocket::TcpSocket; + } +#else + socketType = QAbstractSocket::TcpSocket; +#endif + } else { + if (value == SOCK_DGRAM) + socketType = QAbstractSocket::UdpSocket; + else + socketType = QAbstractSocket::UnknownSocketType; + } } #if defined (QNATIVESOCKETENGINE_DEBUG) QString socketProtocolStr = QStringLiteral("UnknownProtocol"); @@ -1106,6 +1243,7 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters() QString socketTypeStr = QStringLiteral("UnknownSocketType"); if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = QStringLiteral("TcpSocket"); else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = QStringLiteral("UdpSocket"); + else if (socketType == QAbstractSocket::SctpSocket) socketTypeStr = QStringLiteral("SctpSocket"); qDebug("QNativeSocketEnginePrivate::fetchConnectionParameters() local == %s:%i," " peer == %s:%i, socket == %s - %s, inboundStreamCount == %i, outboundStreamCount == %i", diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 0c5b8d9264..9ae2d8ba8f 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -214,6 +214,7 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, switch (opt) { case QNativeSocketEngine::NonBlockingSocketOption: // WSAIoctl case QNativeSocketEngine::TypeOfServiceOption: // not supported + case QNativeSocketEngine::MaxStreamsSocketOption: Q_UNREACHABLE(); case QNativeSocketEngine::ReceiveBufferSocketOption: @@ -325,6 +326,14 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc return -1; } */ + + //### SCTP not implemented + if (socketType == QAbstractSocket::SctpSocket) { + setError(QAbstractSocket::UnsupportedSocketOperationError, + ProtocolUnsupportedErrorString); + return false; + } + QSysInfo::WinVersion osver = QSysInfo::windowsVersion(); //Windows XP and 2003 support IPv6 but not dual stack sockets @@ -451,6 +460,7 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co break; } case QNativeSocketEngine::TypeOfServiceOption: + case QNativeSocketEngine::MaxStreamsSocketOption: return -1; default: @@ -501,6 +511,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt break; } case QNativeSocketEngine::TypeOfServiceOption: + case QNativeSocketEngine::MaxStreamsSocketOption: return false; default: diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp index 9817d4c57d..641863b4fd 100644 --- a/src/network/socket/qnativesocketengine_winrt.cpp +++ b/src/network/socket/qnativesocketengine_winrt.cpp @@ -632,6 +632,7 @@ qint64 QNativeSocketEngine::write(const char *data, qint64 len) qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header, PacketHeaderOptions) { +#ifndef QT_NO_UDPSOCKET Q_D(QNativeSocketEngine); if (d->socketType != QAbstractSocket::UdpSocket || d->pendingDatagrams.isEmpty()) { if (header) @@ -654,10 +655,17 @@ qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHea } memcpy(data, readOrigin, qMin(maxlen, qint64(datagram.data.length()))); return readOrigin.length(); +#else + Q_UNUSED(data) + Q_UNUSED(maxlen) + Q_UNUSED(header) + return -1; +#endif // QT_NO_UDPSOCKET } qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) { +#ifndef QT_NO_UDPSOCKET Q_D(QNativeSocketEngine); if (d->socketType != QAbstractSocket::UdpSocket) return -1; @@ -684,6 +692,12 @@ qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QI Q_ASSERT_SUCCEEDED(hr); return writeIOStream(stream, data, len); +#else + Q_UNUSED(data) + Q_UNUSED(len) + Q_UNUSED(header) + return -1; +#endif // QT_NO_UDPSOCKET } bool QNativeSocketEngine::hasPendingDatagrams() const @@ -1088,6 +1102,7 @@ int QNativeSocketEnginePrivate::option(QAbstractSocketEngine::SocketOption opt) case QAbstractSocketEngine::MulticastTtlOption: case QAbstractSocketEngine::MulticastLoopbackOption: case QAbstractSocketEngine::TypeOfServiceOption: + case QAbstractSocketEngine::MaxStreamsSocketOption: default: return -1; } @@ -1146,6 +1161,7 @@ bool QNativeSocketEnginePrivate::setOption(QAbstractSocketEngine::SocketOption o case QAbstractSocketEngine::MulticastTtlOption: case QAbstractSocketEngine::MulticastLoopbackOption: case QAbstractSocketEngine::TypeOfServiceOption: + case QAbstractSocketEngine::MaxStreamsSocketOption: default: return false; } diff --git a/src/network/socket/qsctpserver.cpp b/src/network/socket/qsctpserver.cpp new file mode 100644 index 0000000000..a2dc824a09 --- /dev/null +++ b/src/network/socket/qsctpserver.cpp @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alex Trotsenko +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QSCTPSERVER_DEBUG + +/*! + \class QSctpServer + \since 5.8 + + \brief The QSctpServer class provides an SCTP-based server. + + \ingroup network + \inmodule QtNetwork + + SCTP (Stream Control Transmission Protocol) is a transport layer + protocol serving in a similar role as the popular protocols TCP + and UDP. Like UDP, SCTP is message-oriented, but it ensures reliable, + in-sequence transport of messages with congestion control like + TCP. See the QSctpSocket documentation for more protocol details. + + QSctpServer is a convenience subclass of QTcpServer that allows + you to accept incoming STCP socket connections either in TCP + emulation or in datagram mode. + + The most common way to use QSctpServer is to construct an object + and set the maximum number of channels that the server is + prepared to support, by calling setMaxChannelCount(). You can set + the TCP emulation mode by passing a negative argument in this + call. Also, a special value of 0 (the default) indicates to use + the peer's value as the actual number of channels. The new incoming + connection inherits this number from the server socket descriptor + and adjusts it according to the remote endpoint settings. + + In TCP emulation mode, accepted clients use a single continuous + byte stream for data transmission, and QSctpServer acts like a + plain QTcpServer. Call nextPendingConnection() to accept the + pending connection as a connected QTcpSocket. The function returns + a pointer to a QTcpSocket in QAbstractSocket::ConnectedState that + you can use for communicating with the client. This mode gives + access only to basic SCTP protocol features. The socket transmits SCTP + packets over IP at system level and interacts through the + QTcpSocket interface with the application. + + In contrast, datagram mode is message-oriented and provides a + complete simultaneous transmission of multiple data streams + between endpoints. Call nextPendingDatagramConnection() to accept + the pending datagram-mode connection as a connected QSctpSocket. + + \note This feature is not supported on the Windows platform. + + \sa QTcpServer, QSctpSocket, QAbstractSocket +*/ + +#include "qsctpserver.h" +#include "qsctpserver_p.h" + +#include "qsctpsocket.h" +#include "qabstractsocketengine_p.h" + +#ifdef QSCTPSERVER_DEBUG +#include +#endif + +QT_BEGIN_NAMESPACE + +/*! \internal +*/ +QSctpServerPrivate::QSctpServerPrivate() + : maxChannelCount(0) +{ +} + +/*! \internal +*/ +QSctpServerPrivate::~QSctpServerPrivate() +{ +} + +/*! \internal +*/ +void QSctpServerPrivate::configureCreatedSocket() +{ + QTcpServerPrivate::configureCreatedSocket(); + if (socketEngine) + socketEngine->setOption(QAbstractSocketEngine::MaxStreamsSocketOption, + maxChannelCount == -1 ? 1 : maxChannelCount); +} + +/*! + Constructs a QSctpServer object. + + Sets the datagram operation mode. The \a parent argument is passed + to QObject's constructor. + + \sa setMaxChannelCount(), listen(), setSocketDescriptor() +*/ +QSctpServer::QSctpServer(QObject *parent) + : QTcpServer(QAbstractSocket::SctpSocket, *new QSctpServerPrivate, parent) +{ +#if defined(QSCTPSERVER_DEBUG) + qDebug("QSctpServer::QSctpServer()"); +#endif +} + +/*! + Destroys the QSctpServer object. If the server is listening for + connections, the socket is automatically closed. + + \sa close() +*/ +QSctpServer::~QSctpServer() +{ +#if defined(QSCTPSERVER_DEBUG) + qDebug("QSctpServer::~QSctpServer()"); +#endif +} + +/*! + Sets the maximum number of channels that the server is prepared to + support in datagram mode, to \a count. If \a count is 0, endpoint + maximum number of channels value would be used. Negative \a count + sets a TCP emulation mode. + + Call this member only when QSctpServer is in UnconnectedState. + + \sa maxChannelCount(), QSctpSocket +*/ +void QSctpServer::setMaxChannelCount(int count) +{ + Q_D(QSctpServer); + if (d->state != QAbstractSocket::UnconnectedState) { + qWarning("QSctpServer::setMaxChannelCount() is only allowed in UnconnectedState"); + return; + } +#if defined(QSCTPSERVER_DEBUG) + qDebug("QSctpServer::setMaxChannelCount(%i)", count); +#endif + d->maxChannelCount = count; +} + +/*! + Returns the maximum number of channels that the accepted sockets are + able to support. + + A value of 0 (the default) means that the number of connection + channels would be set by the remote endpoint. + + Returns -1, if QSctpServer running in TCP emulation mode. + + \sa setMaxChannelCount() +*/ +int QSctpServer::maxChannelCount() const +{ + return d_func()->maxChannelCount; +} + +/*! \reimp +*/ +void QSctpServer::incomingConnection(qintptr socketDescriptor) +{ +#if defined (QSCTPSERVER_DEBUG) + qDebug("QSctpServer::incomingConnection(%i)", socketDescriptor); +#endif + + QSctpSocket *socket = new QSctpSocket(this); + socket->setMaxChannelCount(d_func()->maxChannelCount); + socket->setSocketDescriptor(socketDescriptor); + addPendingConnection(socket); +} + +/*! + Returns the next pending datagram-mode connection as a connected + QSctpSocket object. + + Datagram-mode connection provides a message-oriented, multi-stream + communication. + + The socket is created as a child of the server, which means that + it is automatically deleted when the QSctpServer object is + destroyed. It is still a good idea to delete the object + explicitly when you are done with it, to avoid wasting memory. + + This function returns null if there are no pending datagram-mode + connections. + + \note The returned QSctpSocket object cannot be used from another + thread. If you want to use an incoming connection from another + thread, you need to override incomingConnection(). + + \sa hasPendingConnections(), nextPendingConnection(), QSctpSocket +*/ +QSctpSocket *QSctpServer::nextPendingDatagramConnection() +{ + Q_D(QSctpServer); + + QMutableListIterator i(d->pendingConnections); + while (i.hasNext()) { + QSctpSocket *socket = qobject_cast(i.next()); + Q_ASSERT(socket); + + if (socket->inDatagramMode()) { + i.remove(); + Q_ASSERT(d->socketEngine); + d->socketEngine->setReadNotificationEnabled(true); + return socket; + } + } + + return nullptr; +} + +QT_END_NAMESPACE + +#include "moc_qsctpserver.cpp" diff --git a/src/network/socket/qsctpserver.h b/src/network/socket/qsctpserver.h new file mode 100644 index 0000000000..fb017a924d --- /dev/null +++ b/src/network/socket/qsctpserver.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alex Trotsenko +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCTPSERVER_H +#define QSCTPSERVER_H + +#include + +QT_BEGIN_NAMESPACE + + +#ifndef QT_NO_SCTP + +class QSctpServerPrivate; +class QSctpSocket; + +class Q_NETWORK_EXPORT QSctpServer : public QTcpServer +{ + Q_OBJECT +public: + explicit QSctpServer(QObject *parent = nullptr); + virtual ~QSctpServer(); + + void setMaxChannelCount(int count); + int maxChannelCount() const; + + QSctpSocket *nextPendingDatagramConnection(); + +protected: + void incomingConnection(qintptr handle) Q_DECL_OVERRIDE; + +private: + Q_DISABLE_COPY(QSctpServer) + Q_DECLARE_PRIVATE(QSctpServer) +}; + +#endif // QT_NO_SCTP + +QT_END_NAMESPACE + +#endif // QSCTPSERVER_H diff --git a/src/network/socket/qsctpserver_p.h b/src/network/socket/qsctpserver_p.h new file mode 100644 index 0000000000..32760caffe --- /dev/null +++ b/src/network/socket/qsctpserver_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alex Trotsenko +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCTPSERVER_P_H +#define QSCTPSERVER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qtcpserver_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SCTP + +class QSctpServerPrivate : public QTcpServerPrivate +{ + Q_DECLARE_PUBLIC(QSctpServer) +public: + QSctpServerPrivate(); + virtual ~QSctpServerPrivate(); + + int maxChannelCount; + + void configureCreatedSocket() Q_DECL_OVERRIDE; +}; + +#endif // QT_NO_SCTP + +QT_END_NAMESPACE + +#endif // QSCTPSERVER_P_H diff --git a/src/network/socket/qsctpsocket.cpp b/src/network/socket/qsctpsocket.cpp new file mode 100644 index 0000000000..dcfd6806a4 --- /dev/null +++ b/src/network/socket/qsctpsocket.cpp @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alex Trotsenko +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QSCTPSOCKET_DEBUG + +/*! + \class QSctpSocket + \since 5.8 + + \brief The QSctpSocket class provides an SCTP socket. + + \ingroup network + \inmodule QtNetwork + + SCTP (Stream Control Transmission Protocol) is a transport layer + protocol serving in a similar role as the popular protocols TCP + and UDP. Like UDP, SCTP is message-oriented, but it ensures reliable, + in-sequence transport of messages with congestion control like + TCP. + + SCTP is connection-oriented protocol, which provides the complete + simultaneous transmission of multiple data streams between + endpoints. This multi-streaming allows data to be delivered by + independent channels, so that if there is data loss in one stream, + delivery will not be affected for the other streams. + + As message-oriented, SCTP transports a sequence of messages, + rather than transporting an unbroken stream of bytes as does TCP. + Like in UDP, in SCTP a sender sends a message in one operation, + and that exact message is passed to the receiving application + process in one operation. But unlike UDP, the delivery is + guaranteed. + + It also supports multi-homing, meaning that a connected endpoint + can have alternate IP addresses associated with it in order to + route around network failure or changing conditions. + + QSctpSocket is a convenience subclass of QTcpSocket that allows + you to emulate TCP data stream over SCTP or establish an SCTP + connection for reliable datagram service. + + QSctpSocket can operate in one of two possible modes: + + \list + \li Continuous byte stream (TCP emulation). + \li Multi-streamed datagram mode. + \endlist + + To set a continuous byte stream mode, instantiate QSctpSocket and + call setMaxChannelCount() with the negative number of channels. This + gives the ability to use QSctpSocket as a regular buffered + QTcpSocket. You can call connectToHost() to initiate connection + with endpoint, write() to transmit and read() to receive data from + the peer, but you cannot distinguish message boundaries. + + By default, QSctpSocket operates in datagram mode. Before + connecting, call setMaxChannelCount() to set the maximum number of + channels that the application is prepared to support. This number + is a negotiated parameter with remote endpoint and its value can + be bounded by the operating system. The default value of 0 + indicates to use the peer's value. If both endpoints have default + values, then number of connection channels is system-dependent. + After establishing a connection, you can fetch the actual number + of channels by calling readChannelCount() and writeChannelCount(). + + \snippet code/src_network_socket_qsctpsocket.cpp 0 + + In datagram mode, QSctpSocket performs the buffering of datagrams + independently for each channel. You can queue a datagram to the + buffer of the current channel by calling writeDatagram() and read + a pending datagram by calling readDatagram() respectively. + + Using the standard QIODevice functions read(), readLine(), write(), + etc. is allowed in datagram mode with the same limitations as in + continuous byte stream mode. + + \note This feature is not supported on the Windows platform. + + \sa QSctpServer, QTcpSocket, QAbstractSocket +*/ + +#include "qsctpsocket.h" +#include "qsctpsocket_p.h" + +#include "qabstractsocketengine_p.h" +#include "private/qbytearray_p.h" + +#ifdef QSCTPSOCKET_DEBUG +#include +#endif + +QT_BEGIN_NAMESPACE + +/*! \internal +*/ +QSctpSocketPrivate::QSctpSocketPrivate() + : maxChannelCount(0) +{ +} + +/*! \internal +*/ +QSctpSocketPrivate::~QSctpSocketPrivate() +{ +} + +/*! \internal +*/ +bool QSctpSocketPrivate::canReadNotification() +{ + Q_Q(QSctpSocket); +#if defined (QSCTPSOCKET_DEBUG) + qDebug("QSctpSocketPrivate::canReadNotification()"); +#endif + + // Handle TCP emulation mode in the base implementation. + if (!q->inDatagramMode()) + return QTcpSocketPrivate::canReadNotification(); + + const int savedCurrentChannel = currentReadChannel; + bool currentChannelRead = false; + do { + int datagramSize = incomingDatagram.size(); + QIpPacketHeader header; + + do { + // Determine the size of the pending datagram. + qint64 bytesToRead = socketEngine->bytesAvailable(); + if (bytesToRead == 0) + bytesToRead = 4096; + + Q_ASSERT((datagramSize + int(bytesToRead)) < MaxByteArraySize); + incomingDatagram.resize(datagramSize + int(bytesToRead)); + +#if defined (QSCTPSOCKET_DEBUG) + qDebug("QSctpSocketPrivate::canReadNotification() about to read %lli bytes", + bytesToRead); +#endif + qint64 readBytes = socketEngine->readDatagram( + incomingDatagram.data() + datagramSize, bytesToRead, &header, + QAbstractSocketEngine::WantAll); + if (readBytes <= 0) { + if (readBytes == -2) { // no data available for reading + incomingDatagram.resize(datagramSize); + return currentChannelRead; + } + + socketEngine->close(); + if (readBytes == 0) { + setErrorAndEmit(QAbstractSocket::RemoteHostClosedError, + QSctpSocket::tr("The remote host closed the connection")); + } else { +#if defined (QSCTPSOCKET_DEBUG) + qDebug("QSctpSocketPrivate::canReadNotification() read failed: %s", + socketEngine->errorString().toLatin1().constData()); +#endif + setErrorAndEmit(socketEngine->error(), socketEngine->errorString()); + } + +#if defined (QSCTPSOCKET_DEBUG) + qDebug("QSctpSocketPrivate::canReadNotification() disconnecting socket"); +#endif + q->disconnectFromHost(); + return currentChannelRead; + } + datagramSize += int(readBytes); // update datagram size + } while (!header.endOfRecord); + +#if defined (QSCTPSOCKET_DEBUG) + qDebug("QSctpSocketPrivate::canReadNotification() got datagram from channel %i, size = %i", + header.streamNumber, datagramSize); +#endif + + // Drop the datagram, if opened only for writing + if (!q->isReadable()) { + incomingDatagram.clear(); + continue; + } + + // Store datagram in the channel buffer + Q_ASSERT(header.streamNumber < readBuffers.size()); + incomingDatagram.resize(datagramSize); + readBuffers[header.streamNumber].setChunkSize(0); // set packet mode on channel buffer + readBuffers[header.streamNumber].append(incomingDatagram); + incomingDatagram = QByteArray(); + + if (readHeaders.size() != readBuffers.size()) + readHeaders.resize(readBuffers.size()); + readHeaders[header.streamNumber].push_back(header); + + // Emit notifications. + if (header.streamNumber == savedCurrentChannel) + currentChannelRead = true; + emitReadyRead(header.streamNumber); + + } while (state == QAbstractSocket::ConnectedState); + + return currentChannelRead; +} + +/*! \internal +*/ +bool QSctpSocketPrivate::writeToSocket() +{ + Q_Q(QSctpSocket); +#if defined (QSCTPSOCKET_DEBUG) + qDebug("QSctpSocketPrivate::writeToSocket()"); +#endif + + // Handle TCP emulation mode in the base implementation. + if (!q->inDatagramMode()) + return QTcpSocketPrivate::writeToSocket(); + + if (!socketEngine) + return false; + + QIpPacketHeader defaultHeader; + const int savedCurrentChannel = currentWriteChannel; + bool currentChannelWritten = false; + bool transmitting; + do { + transmitting = false; + + for (int channel = 0; channel < writeBuffers.size(); ++channel) { + QRingBuffer &channelBuffer = writeBuffers[channel]; + + if (channelBuffer.isEmpty()) + continue; + + const bool hasHeader = (channel < writeHeaders.size()) + && !writeHeaders[channel].empty(); + QIpPacketHeader &header = hasHeader ? writeHeaders[channel].front() : defaultHeader; + header.streamNumber = channel; + qint64 sent = socketEngine->writeDatagram(channelBuffer.readPointer(), + channelBuffer.nextDataBlockSize(), + header); + if (sent < 0) { + if (sent == -2) // temporary error in writeDatagram + return currentChannelWritten; + + socketEngine->close(); +#if defined (QSCTPSOCKET_DEBUG) + qDebug("QSctpSocketPrivate::writeToSocket() write error, aborting. %s", + socketEngine->errorString().toLatin1().constData()); +#endif + setErrorAndEmit(socketEngine->error(), socketEngine->errorString()); + // An unexpected error so close the socket. + q->disconnectFromHost(); + return currentChannelWritten; + } + Q_ASSERT(sent == channelBuffer.nextDataBlockSize()); +#if defined (QSCTPSOCKET_DEBUG) + qDebug("QSctpSocketPrivate::writeToSocket() sent datagram of size %lli to channel %i", + sent, channel); +#endif + transmitting = true; + + // Remove datagram from the buffer + channelBuffer.read(); + if (hasHeader) + writeHeaders[channel].pop_front(); + + // Emit notifications. + if (channel == savedCurrentChannel) + currentChannelWritten = true; + emitBytesWritten(sent, channel); + + // If we were closed as a result of the bytesWritten() signal, return. + if (state == QAbstractSocket::UnconnectedState) { +#if defined (QSCTPSOCKET_DEBUG) + qDebug("QSctpSocketPrivate::writeToSocket() socket is closing - returning"); +#endif + return currentChannelWritten; + } + } + } while (transmitting); + + // At this point socket is either in Connected or Closing state, + // write buffers are empty. + if (state == QAbstractSocket::ClosingState) + q->disconnectFromHost(); + else + socketEngine->setWriteNotificationEnabled(false); + + return currentChannelWritten; +} + +/*! \internal +*/ +void QSctpSocketPrivate::configureCreatedSocket() +{ + if (socketEngine) + socketEngine->setOption(QAbstractSocketEngine::MaxStreamsSocketOption, + maxChannelCount < 0 ? 1 : maxChannelCount); +} + +/*! + Creates a QSctpSocket object in state \c UnconnectedState. + + Sets the datagram operation mode. The \a parent argument is passed + to QObject's constructor. + + \sa socketType(), setMaxChannelCount() +*/ +QSctpSocket::QSctpSocket(QObject *parent) + : QTcpSocket(SctpSocket, *new QSctpSocketPrivate, parent) +{ +#if defined(QSCTPSOCKET_DEBUG) + qDebug("QSctpSocket::QSctpSocket()"); +#endif + d_func()->isBuffered = true; +} + +/*! + Destroys the socket, closing the connection if necessary. + + \sa close() +*/ +QSctpSocket::~QSctpSocket() +{ +#if defined(QSCTPSOCKET_DEBUG) + qDebug("QSctpSocket::~QSctpSocket()"); +#endif +} + +/*! \reimp +*/ +qint64 QSctpSocket::readData(char *data, qint64 maxSize) +{ + Q_D(QSctpSocket); + + // Cleanup headers, if the user calls the standard QIODevice functions + if (d->currentReadChannel < d->readHeaders.size()) + d->readHeaders[d->currentReadChannel].clear(); + + return QTcpSocket::readData(data, maxSize); +} + +/*! \reimp +*/ +qint64 QSctpSocket::readLineData(char *data, qint64 maxlen) +{ + Q_D(QSctpSocket); + + // Cleanup headers, if the user calls the standard QIODevice functions + if (d->currentReadChannel < d->readHeaders.size()) + d->readHeaders[d->currentReadChannel].clear(); + + return QTcpSocket::readLineData(data, maxlen); +} + +/*! \reimp +*/ +void QSctpSocket::close() +{ + QTcpSocket::close(); + d_func()->readHeaders.clear(); +} + +/*! \reimp +*/ +void QSctpSocket::disconnectFromHost() +{ + Q_D(QSctpSocket); + + QTcpSocket::disconnectFromHost(); + if (d->state == QAbstractSocket::UnconnectedState) { + d->incomingDatagram.clear(); + d->writeHeaders.clear(); + } +} + +/*! + Sets the maximum number of channels that the application is + prepared to support in datagram mode, to \a count. If \a count + is 0, endpoint's value for maximum number of channels is used. + Negative \a count sets a continuous byte stream mode. + + Call this member only when QSctpSocket is in UnconnectedState. + + \sa maxChannelCount(), readChannelCount(), writeChannelCount() +*/ +void QSctpSocket::setMaxChannelCount(int count) +{ + Q_D(QSctpSocket); + if (d->state != QAbstractSocket::UnconnectedState) { + qWarning("QSctpSocket::setMaxChannelCount() is only allowed in UnconnectedState"); + return; + } +#if defined(QSCTPSOCKET_DEBUG) + qDebug("QSctpSocket::setMaxChannelCount(%i)", count); +#endif + d->maxChannelCount = qMax(count, -1); +} + +/*! + Returns the maximum number of channels that QSctpSocket is able to + support. + + A value of 0 (the default) means that the number of connection + channels would be set by the remote endpoint. + + Returns -1 if QSctpSocket is running in continuous byte stream + mode. + + \sa setMaxChannelCount(), readChannelCount(), writeChannelCount() +*/ +int QSctpSocket::maxChannelCount() const +{ + return d_func()->maxChannelCount; +} + +/*! + Returns \c true if the socket is running in datagram mode. + + \sa setMaxChannelCount() +*/ +bool QSctpSocket::inDatagramMode() const +{ + Q_D(const QSctpSocket); + return d->maxChannelCount != -1 && d->isBuffered; +} + +/*! + Reads a datagram from the buffer of the current read channel, and + returns it as a QNetworkDatagram object, along with the sender's + host address and port. If possible, this function will also try to + determine the datagram's destination address, port, and the number + of hop counts at reception time. + + On failure, returns a QNetworkDatagram that reports \l + {QNetworkDatagram::isValid()}{not valid}. + + \sa writeDatagram(), inDatagramMode(), currentReadChannel() +*/ +QNetworkDatagram QSctpSocket::readDatagram() +{ + Q_D(QSctpSocket); + + if (!isReadable() || !inDatagramMode()) { + qWarning("QSctpSocket::readDatagram(): operation is not permitted"); + return QNetworkDatagram(); + } + + if (d->currentReadChannel >= d->readHeaders.size() + || (d->readHeaders[d->currentReadChannel].size() == 0)) { + Q_ASSERT(d->buffer.isEmpty()); + return QNetworkDatagram(); + } + + QNetworkDatagram result(*new QNetworkDatagramPrivate(d->buffer.read(), + d->readHeaders[d->currentReadChannel].front())); + d->readHeaders[d->currentReadChannel].pop_front(); + +#if defined (QSCTPSOCKET_DEBUG) + qDebug("QSctpSocket::readDatagram() returning datagram (%p, %i, \"%s\", %i)", + result.d->data.constData(), + result.d->data.size(), + result.senderAddress().toString().toLatin1().constData(), + result.senderPort()); +#endif + + return result; +} + +/*! + Writes a datagram to the buffer of the current write channel. + Returns true on success; otherwise returns false. + + \sa readDatagram(), inDatagramMode(), currentWriteChannel() +*/ +bool QSctpSocket::writeDatagram(const QNetworkDatagram &datagram) +{ + Q_D(QSctpSocket); + + if (!isWritable() || d->state != QAbstractSocket::ConnectedState || !d->socketEngine + || !d->socketEngine->isValid() || !inDatagramMode()) { + qWarning("QSctpSocket::writeDatagram(): operation is not permitted"); + return false; + } + + if (datagram.d->data.isEmpty()) { + qWarning("QSctpSocket::writeDatagram() is called with empty datagram"); + return false; + } + + +#if defined QSCTPSOCKET_DEBUG + qDebug("QSctpSocket::writeDatagram(%p, %i, \"%s\", %i)", + datagram.d->data.constData(), + datagram.d->data.size(), + datagram.destinationAddress().toString().toLatin1().constData(), + datagram.destinationPort()); +#endif + + if (d->writeHeaders.size() != d->writeBuffers.size()) + d->writeHeaders.resize(d->writeBuffers.size()); + Q_ASSERT(d->currentWriteChannel < d->writeHeaders.size()); + d->writeHeaders[d->currentWriteChannel].push_back(datagram.d->header); + d->writeBuffer.setChunkSize(0); // set packet mode on channel buffer + d->writeBuffer.append(datagram.d->data); + + d->socketEngine->setWriteNotificationEnabled(true); + return true; +} + +QT_END_NAMESPACE diff --git a/src/network/socket/qsctpsocket.h b/src/network/socket/qsctpsocket.h new file mode 100644 index 0000000000..aaa4e1ecca --- /dev/null +++ b/src/network/socket/qsctpsocket.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alex Trotsenko +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCTPSOCKET_H +#define QSCTPSOCKET_H + +#include +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SCTP + +class QSctpSocketPrivate; + +class Q_NETWORK_EXPORT QSctpSocket : public QTcpSocket +{ + Q_OBJECT +public: + explicit QSctpSocket(QObject *parent = nullptr); + virtual ~QSctpSocket(); + + void close() Q_DECL_OVERRIDE; + void disconnectFromHost() Q_DECL_OVERRIDE; + + void setMaxChannelCount(int count); + int maxChannelCount() const; + bool inDatagramMode() const; + + QNetworkDatagram readDatagram(); + bool writeDatagram(const QNetworkDatagram &datagram); + +protected: + qint64 readData(char *data, qint64 maxlen) Q_DECL_OVERRIDE; + qint64 readLineData(char *data, qint64 maxlen) Q_DECL_OVERRIDE; + +private: + Q_DISABLE_COPY(QSctpSocket) + Q_DECLARE_PRIVATE(QSctpSocket) +}; + +#endif // QT_NO_SCTP + +QT_END_NAMESPACE + +#endif // QSCTPSOCKET_H diff --git a/src/network/socket/qsctpsocket_p.h b/src/network/socket/qsctpsocket_p.h new file mode 100644 index 0000000000..f38095330f --- /dev/null +++ b/src/network/socket/qsctpsocket_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Alex Trotsenko +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCTPSOCKET_P_H +#define QSCTPSOCKET_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SCTP + +class QSctpSocketPrivate : public QTcpSocketPrivate +{ + Q_DECLARE_PUBLIC(QSctpSocket) +public: + QSctpSocketPrivate(); + virtual ~QSctpSocketPrivate(); + + bool canReadNotification() Q_DECL_OVERRIDE; + bool writeToSocket() Q_DECL_OVERRIDE; + + QByteArray incomingDatagram; + int maxChannelCount; + + typedef std::deque IpHeaderList; + QVector readHeaders; + QVector writeHeaders; + + void configureCreatedSocket() Q_DECL_OVERRIDE; +}; + +#endif // QT_NO_SCTP + +QT_END_NAMESPACE + +#endif // QSCTPSOCKET_P_H diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index ee3e0d9f0e..518ec21f90 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1605,8 +1605,31 @@ bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &) } #endif // QT_NO_NETWORKINTERFACE +bool QSocks5SocketEngine::hasPendingDatagrams() const +{ + Q_D(const QSocks5SocketEngine); + Q_INIT_CHECK(false); + + d->checkForDatagrams(); + + return !d->udpData->pendingDatagrams.isEmpty(); +} + +qint64 QSocks5SocketEngine::pendingDatagramSize() const +{ + Q_D(const QSocks5SocketEngine); + + d->checkForDatagrams(); + + if (!d->udpData->pendingDatagrams.isEmpty()) + return d->udpData->pendingDatagrams.head().data.size(); + return 0; +} +#endif // QT_NO_UDPSOCKET + qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header, PacketHeaderOptions) { +#ifndef QT_NO_UDPSOCKET Q_D(QSocks5SocketEngine); d->checkForDatagrams(); @@ -1620,10 +1643,17 @@ qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHea header->senderAddress = datagram.address; header->senderPort = datagram.port; return copyLen; +#else + Q_UNUSED(data) + Q_UNUSED(maxlen) + Q_UNUSED(header) + return -1; +#endif // QT_NO_UDPSOCKET } qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) { +#ifndef QT_NO_UDPSOCKET Q_D(QSocks5SocketEngine); // it is possible to send with out first binding with udp, but socks5 requires a bind. @@ -1660,29 +1690,13 @@ qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QI } return len; -} - -bool QSocks5SocketEngine::hasPendingDatagrams() const -{ - Q_D(const QSocks5SocketEngine); - Q_INIT_CHECK(false); - - d->checkForDatagrams(); - - return !d->udpData->pendingDatagrams.isEmpty(); -} - -qint64 QSocks5SocketEngine::pendingDatagramSize() const -{ - Q_D(const QSocks5SocketEngine); - - d->checkForDatagrams(); - - if (!d->udpData->pendingDatagrams.isEmpty()) - return d->udpData->pendingDatagrams.head().data.size(); - return 0; -} +#else + Q_UNUSED(data) + Q_UNUSED(len) + Q_UNUSED(header) + return -1; #endif // QT_NO_UDPSOCKET +} qint64 QSocks5SocketEngine::bytesToWrite() const { diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h index bff7e9ecac..864b163489 100644 --- a/src/network/socket/qsocks5socketengine_p.h +++ b/src/network/socket/qsocks5socketengine_p.h @@ -100,13 +100,13 @@ public: bool setMulticastInterface(const QNetworkInterface &iface) Q_DECL_OVERRIDE; #endif // QT_NO_NETWORKINTERFACE - qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0, - PacketHeaderOptions = WantNone) Q_DECL_OVERRIDE; - qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE; bool hasPendingDatagrams() const Q_DECL_OVERRIDE; qint64 pendingDatagramSize() const Q_DECL_OVERRIDE; #endif // QT_NO_UDPSOCKET + qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0, + PacketHeaderOptions = WantNone) Q_DECL_OVERRIDE; + qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE; qint64 bytesToWrite() const Q_DECL_OVERRIDE; int option(SocketOption option) const Q_DECL_OVERRIDE; diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp index d9ffdbd214..eddf789921 100644 --- a/src/network/socket/qtcpserver.cpp +++ b/src/network/socket/qtcpserver.cpp @@ -119,6 +119,7 @@ QT_BEGIN_NAMESPACE */ QTcpServerPrivate::QTcpServerPrivate() : port(0) + , socketType(QAbstractSocket::UnknownSocketType) , state(QAbstractSocket::UnconnectedState) , socketEngine(0) , serverSocketError(QAbstractSocket::UnknownSocketError) @@ -148,13 +149,21 @@ QNetworkProxy QTcpServerPrivate::resolveProxy(const QHostAddress &address, quint proxies << proxy; } else { // try the application settings instead - QNetworkProxyQuery query(port, QString(), QNetworkProxyQuery::TcpServer); + QNetworkProxyQuery query(port, QString(), + socketType == QAbstractSocket::SctpSocket ? + QNetworkProxyQuery::SctpServer : + QNetworkProxyQuery::TcpServer); proxies = QNetworkProxyFactory::proxyForQuery(query); } // return the first that we can use for (const QNetworkProxy &p : qAsConst(proxies)) { - if (p.capabilities() & QNetworkProxy::ListeningCapability) + if (socketType == QAbstractSocket::TcpSocket && + (p.capabilities() & QNetworkProxy::ListeningCapability) != 0) + return p; + + if (socketType == QAbstractSocket::SctpSocket && + (p.capabilities() & QNetworkProxy::SctpListeningCapability) != 0) return p; } @@ -228,9 +237,11 @@ void QTcpServerPrivate::readNotification() QTcpServer::QTcpServer(QObject *parent) : QObject(*new QTcpServerPrivate, parent) { + Q_D(QTcpServer); #if defined(QTCPSERVER_DEBUG) qDebug("QTcpServer::QTcpServer(%p)", parent); #endif + d->socketType = QAbstractSocket::TcpSocket; } /*! @@ -251,13 +262,22 @@ QTcpServer::~QTcpServer() } /*! \internal + + Constructs a new server object with socket of type \a socketType. The \a + parent argument is passed to QObject's constructor. */ -QTcpServer::QTcpServer(QTcpServerPrivate &dd, QObject *parent) - : QObject(dd, parent) +QTcpServer::QTcpServer(QAbstractSocket::SocketType socketType, QTcpServerPrivate &dd, + QObject *parent) : QObject(dd, parent) { + Q_D(QTcpServer); #if defined(QTCPSERVER_DEBUG) - qDebug("QTcpServer::QTcpServer(QTcpServerPrivate == %p, parent == %p)", &dd, parent); + qDebug("QTcpServer::QTcpServer(%sSocket, QTcpServerPrivate == %p, parent == %p)", + socketType == QAbstractSocket::TcpSocket ? "Tcp" + : socketType == QAbstractSocket::UdpSocket ? "Udp" + : socketType == QAbstractSocket::SctpSocket ? "Sctp" + : "Unknown", &dd, parent); #endif + d->socketType = socketType; } /*! @@ -288,7 +308,7 @@ bool QTcpServer::listen(const QHostAddress &address, quint16 port) #endif delete d->socketEngine; - d->socketEngine = QAbstractSocketEngine::createSocketEngine(QAbstractSocket::TcpSocket, proxy, this); + d->socketEngine = QAbstractSocketEngine::createSocketEngine(d->socketType, proxy, this); if (!d->socketEngine) { d->serverSocketError = QAbstractSocket::UnsupportedSocketOperationError; d->serverSocketErrorString = tr("Operation on socket is not supported"); @@ -298,7 +318,7 @@ bool QTcpServer::listen(const QHostAddress &address, quint16 port) //copy network session down to the socket engine (if it has been set) d->socketEngine->setProperty("_q_networksession", property("_q_networksession")); #endif - if (!d->socketEngine->initialize(QAbstractSocket::TcpSocket, proto)) { + if (!d->socketEngine->initialize(d->socketType, proto)) { d->serverSocketError = d->socketEngine->error(); d->serverSocketErrorString = d->socketEngine->errorString(); return false; diff --git a/src/network/socket/qtcpserver.h b/src/network/socket/qtcpserver.h index 34cf9ea9d1..192cbce54c 100644 --- a/src/network/socket/qtcpserver.h +++ b/src/network/socket/qtcpserver.h @@ -94,7 +94,8 @@ protected: virtual void incomingConnection(qintptr handle); void addPendingConnection(QTcpSocket* socket); - QTcpServer(QTcpServerPrivate &dd, QObject *parent = Q_NULLPTR); + QTcpServer(QAbstractSocket::SocketType socketType, QTcpServerPrivate &dd, + QObject *parent = Q_NULLPTR); Q_SIGNALS: void newConnection(); diff --git a/src/network/socket/qtcpserver_p.h b/src/network/socket/qtcpserver_p.h index 47f45a8404..b11dd93718 100644 --- a/src/network/socket/qtcpserver_p.h +++ b/src/network/socket/qtcpserver_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Alex Trotsenko ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -74,6 +75,7 @@ public: quint16 port; QHostAddress address; + QAbstractSocket::SocketType socketType; QAbstractSocket::SocketState state; QAbstractSocketEngine *socketEngine; diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index d78379fb39..37b385dfb5 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -347,6 +347,12 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre if (sent >= 0) { emit bytesWritten(sent); } else { + if (sent == -2) { + // Socket engine reports EAGAIN. Treat as a temporary error. + d->setErrorAndEmit(QAbstractSocket::TemporaryError, + tr("Unable to send a datagram")); + return -1; + } d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString()); } return sent; @@ -495,8 +501,15 @@ qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *addres d->hasPendingData = false; d->socketEngine->setReadNotificationEnabled(true); - if (readBytes < 0) + if (readBytes < 0) { + if (readBytes == -2) { + // No pending datagram. Treat as a temporary error. + d->setErrorAndEmit(QAbstractSocket::TemporaryError, + tr("No datagram available for reading")); + return -1; + } d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString()); + } return readBytes; } diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri index 2e3e26d37d..b1c0b6bd6e 100644 --- a/src/network/socket/socket.pri +++ b/src/network/socket/socket.pri @@ -25,6 +25,18 @@ SOURCES += socket/qabstractsocketengine.cpp \ socket/qlocalsocket.cpp \ socket/qlocalserver.cpp +# SCTP support. + +contains(QT_CONFIG, sctp) { + HEADERS += socket/qsctpserver.h \ + socket/qsctpserver_p.h \ + socket/qsctpsocket.h \ + socket/qsctpsocket_p.h + + SOURCES += socket/qsctpserver.cpp \ + socket/qsctpsocket.cpp +} + !winrt { SOURCES += socket/qnativesocketengine.cpp HEADERS += socket/qnativesocketengine_p.h -- cgit v1.2.3