summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlex Trotsenko <alex1973tr@gmail.com>2014-09-24 17:31:33 +0300
committerAlex Trotsenko <alex1973tr@gmail.com>2016-07-27 06:37:24 +0000
commit75a9bd2a4f637fb8e8e3dc4609a7045547119e80 (patch)
tree73b8a0a950eb87a7407edada3ea6547f08baf328 /src
parentc5a4b093d051680bf7f34bb5acff16eea9d4979f (diff)
Introduce SCTP sockets support
Add protocol-specific code and the QSctpServer, QSctpSocket classes. Change-Id: Ie9a1d87bd1fda866a2405043d1c15c12ded5a96e Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src')
-rw-r--r--src/corelib/io/qiodevice.cpp12
-rw-r--r--src/corelib/io/qiodevice_p.h2
-rw-r--r--src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp53
-rw-r--r--src/network/kernel/qnetworkdatagram.h1
-rw-r--r--src/network/kernel/qnetworkdatagram_p.h10
-rw-r--r--src/network/kernel/qnetworkproxy.cpp37
-rw-r--r--src/network/kernel/qnetworkproxy.h8
-rw-r--r--src/network/kernel/qnetworkproxy_win.cpp10
-rw-r--r--src/network/network.pro1
-rw-r--r--src/network/socket/qabstractsocket.cpp73
-rw-r--r--src/network/socket/qabstractsocket.h1
-rw-r--r--src/network/socket/qabstractsocket_p.h7
-rw-r--r--src/network/socket/qabstractsocketengine_p.h11
-rw-r--r--src/network/socket/qhttpsocketengine.cpp22
-rw-r--r--src/network/socket/qhttpsocketengine_p.h6
-rw-r--r--src/network/socket/qnativesocketengine.cpp40
-rw-r--r--src/network/socket/qnativesocketengine_p.h6
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp182
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp11
-rw-r--r--src/network/socket/qnativesocketengine_winrt.cpp16
-rw-r--r--src/network/socket/qsctpserver.cpp250
-rw-r--r--src/network/socket/qsctpserver.h77
-rw-r--r--src/network/socket/qsctpserver_p.h76
-rw-r--r--src/network/socket/qsctpsocket.cpp543
-rw-r--r--src/network/socket/qsctpsocket.h82
-rw-r--r--src/network/socket/qsctpsocket_p.h90
-rw-r--r--src/network/socket/qsocks5socketengine.cpp58
-rw-r--r--src/network/socket/qsocks5socketengine_p.h6
-rw-r--r--src/network/socket/qtcpserver.cpp34
-rw-r--r--src/network/socket/qtcpserver.h3
-rw-r--r--src/network/socket/qtcpserver_p.h2
-rw-r--r--src/network/socket/qudpsocket.cpp15
-rw-r--r--src/network/socket/socket.pri12
33 files changed, 1640 insertions, 117 deletions
diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp
index 94fb68450c..fbee1a223f 100644
--- a/src/corelib/io/qiodevice.cpp
+++ b/src/corelib/io/qiodevice.cpp
@@ -739,6 +739,18 @@ void QIODevicePrivate::setWriteChannelCount(int count)
}
/*!
+ \internal
+*/
+bool QIODevicePrivate::allWriteBuffersEmpty() const
+{
+ for (const QRingBuffer &ringBuffer : writeBuffers) {
+ if (!ringBuffer.isEmpty())
+ return false;
+ }
+ return true;
+}
+
+/*!
Opens the device and sets its OpenMode to \a mode. Returns \c true if successful;
otherwise returns \c false. This function should be called from any
reimplementations of open() or other functions that open the device.
diff --git a/src/corelib/io/qiodevice_p.h b/src/corelib/io/qiodevice_p.h
index eed98a8fe3..76bec89ef2 100644
--- a/src/corelib/io/qiodevice_p.h
+++ b/src/corelib/io/qiodevice_p.h
@@ -154,6 +154,8 @@ public:
return buffer.isEmpty() || (transactionStarted && isSequential()
&& transactionPos == buffer.size());
}
+ bool allWriteBuffersEmpty() const;
+
void seekBuffer(qint64 newPos);
inline void setCurrentReadChannel(int channel)
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 <alex1973tr@gmail.com>
+** 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) |
@@ -966,6 +976,14 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
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
incoming connections from the network. Normally, only the
@@ -981,6 +999,14 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::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<QNetworkProxyQueryPrivate>::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<QNetworkProxy> filterProxyListByCapabilities(const QList<QNetworkPr
case QNetworkProxyQuery::UdpSocket:
requiredCaps = QNetworkProxy::UdpTunnelingCapability;
break;
+ case QNetworkProxyQuery::SctpSocket:
+ requiredCaps = QNetworkProxy::SctpTunnelingCapability;
+ break;
case QNetworkProxyQuery::TcpServer:
requiredCaps = QNetworkProxy::ListeningCapability;
break;
+ case QNetworkProxyQuery::SctpServer:
+ requiredCaps = QNetworkProxy::SctpListeningCapability;
+ break;
default:
return proxyList;
break;
@@ -281,7 +287,9 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
QList<QNetworkProxy> result;
QHash<QString, QNetworkProxy> 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<bool> 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<bool> 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<bool> 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 <netinet/tcp.h>
+#ifndef QT_NO_SCTP
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/sctp.h>
+#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;
}
@@ -830,6 +881,9 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
#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)];
struct msghdr msg;
@@ -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<int *>(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<sctp_sndrcvinfo *>(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<struct cmsghdr *>(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<sctp_sndrcvinfo *>(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<cmsghdr *>(reinterpret_cast<char *>(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 <alex1973tr@gmail.com>
+** 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 <qdebug.h>
+#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<QTcpSocket *> i(d->pendingConnections);
+ while (i.hasNext()) {
+ QSctpSocket *socket = qobject_cast<QSctpSocket *>(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 <alex1973tr@gmail.com>
+** 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 <QtNetwork/qtcpserver.h>
+
+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 <alex1973tr@gmail.com>
+** 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 <alex1973tr@gmail.com>
+** 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 <qdebug.h>
+#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 <alex1973tr@gmail.com>
+** 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 <QtNetwork/qtcpsocket.h>
+#include <QtNetwork/qnetworkdatagram.h>
+
+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 <alex1973tr@gmail.com>
+** 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 <QtNetwork/qsctpsocket.h>
+#include <private/qtcpsocket_p.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qvector.h>
+#include <private/qnetworkdatagram_p.h>
+
+#include <deque>
+
+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<QIpPacketHeader> IpHeaderList;
+ QVector<IpHeaderList> readHeaders;
+ QVector<IpHeaderList> 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 <alex1973tr@gmail.com>
** 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