summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.tests/unix/sctp/sctp.cpp60
-rw-r--r--config.tests/unix/sctp/sctp.pro4
-rw-r--r--config_help.txt3
-rw-r--r--configure.json13
-rw-r--r--examples/network/multistreamclient/chatconsumer.cpp83
-rw-r--r--examples/network/multistreamclient/chatconsumer.h69
-rw-r--r--examples/network/multistreamclient/client.cpp207
-rw-r--r--examples/network/multistreamclient/client.h90
-rw-r--r--examples/network/multistreamclient/consumer.h65
-rw-r--r--examples/network/multistreamclient/main.cpp50
-rw-r--r--examples/network/multistreamclient/movieconsumer.cpp73
-rw-r--r--examples/network/multistreamclient/movieconsumer.h64
-rw-r--r--examples/network/multistreamclient/multistreamclient.pro16
-rw-r--r--examples/network/multistreamclient/timeconsumer.cpp84
-rw-r--r--examples/network/multistreamclient/timeconsumer.h69
-rw-r--r--examples/network/multistreamserver/animation.gifbin0 -> 42629 bytes
-rw-r--r--examples/network/multistreamserver/chatprovider.cpp67
-rw-r--r--examples/network/multistreamserver/chatprovider.h57
-rw-r--r--examples/network/multistreamserver/main.cpp51
-rw-r--r--examples/network/multistreamserver/movieprovider.cpp63
-rw-r--r--examples/network/multistreamserver/movieprovider.h63
-rw-r--r--examples/network/multistreamserver/multistreamserver.pro24
-rw-r--r--examples/network/multistreamserver/provider.h66
-rw-r--r--examples/network/multistreamserver/server.cpp159
-rw-r--r--examples/network/multistreamserver/server.h89
-rw-r--r--examples/network/multistreamserver/timeprovider.cpp67
-rw-r--r--examples/network/multistreamserver/timeprovider.h55
-rw-r--r--examples/network/network.pro1
-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
-rw-r--r--tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp4
-rw-r--r--tests/auto/network/socket/qsctpsocket/qsctpsocket.pro6
-rw-r--r--tests/auto/network/socket/qsctpsocket/tst_qsctpsocket.cpp489
-rw-r--r--tests/auto/network/socket/socket.pro4
-rw-r--r--tools/configure/configureapp.cpp20
66 files changed, 3873 insertions, 119 deletions
diff --git a/config.tests/unix/sctp/sctp.cpp b/config.tests/unix/sctp/sctp.cpp
new file mode 100644
index 0000000000..40d7bb1bcb
--- /dev/null
+++ b/config.tests/unix/sctp/sctp.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the config.tests 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$
+**
+****************************************************************************/
+
+/* Sample program for configure to test for SCTP sockets support
+on target platforms. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/sctp.h>
+
+int main()
+{
+ int fd;
+ sctp_initmsg sctpInitMsg;
+ socklen_t sctpInitMsgSize = sizeof(sctpInitMsg);
+
+ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
+ if (fd == -1 || getsockopt(fd, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg,
+ &sctpInitMsgSize) != 0)
+ return 1;
+
+ return 0;
+}
diff --git a/config.tests/unix/sctp/sctp.pro b/config.tests/unix/sctp/sctp.pro
new file mode 100644
index 0000000000..edcc0a444a
--- /dev/null
+++ b/config.tests/unix/sctp/sctp.pro
@@ -0,0 +1,4 @@
+SOURCES = sctp.cpp
+CONFIG -= qt
+QT =
+LIBS += $$QMAKE_LIBS_NETWORK
diff --git a/config_help.txt b/config_help.txt
index b13dbe3c27..2ea2a812e4 100644
--- a/config_help.txt
+++ b/config_help.txt
@@ -365,6 +365,9 @@ Additional options:
-no-system-proxies ... Do not use system network proxies by default.
* -system-proxies ...... Use system network proxies by default.
+ * -no-sctp ............. Do not compile SCTP network protocol support.
+ -sctp ................ Compile SCTP support.
+
-no-warnings-are-errors Make warnings be treated normally
-warnings-are-errors Make warnings be treated as errors
(enabled if -developer-build is active)
diff --git a/configure.json b/configure.json
index a062477f53..3013a84ccf 100644
--- a/configure.json
+++ b/configure.json
@@ -130,6 +130,7 @@
"release": { "type": "enum", "name": "debug", "values": { "yes": "no", "no": "yes" } },
"rpath": "boolean",
"sanitize": "sanitize",
+ "sctp": "boolean",
"sdk": "string",
"securetransport": "boolean",
"separate-debug-info": { "type": "boolean", "name": "separate_debug_info" },
@@ -596,6 +597,11 @@
"type": "openssl",
"libs": "-lssl -lcrypto"
},
+ "sctp": {
+ "description": "SCTP support",
+ "type": "compile",
+ "test": "unix/sctp"
+ },
"icu": {
"description": "ICU",
"type": "compile",
@@ -1625,6 +1631,12 @@
"condition": "features.securetransport || features.openssl",
"output": [ "feature" ]
},
+ "sctp": {
+ "description": "SCTP",
+ "autoDetect": false,
+ "condition": "tests.sctp",
+ "output": [ "feature" ]
+ },
"accessibility": {
"description": "Accessibility",
"output": [ "feature" ]
@@ -2462,6 +2474,7 @@ Please apply the patch corresponding to your Standard Library vendor, found in
},
"openssl",
"openssl-linked",
+ "sctp",
"system-proxies"
]
},
diff --git a/examples/network/multistreamclient/chatconsumer.cpp b/examples/network/multistreamclient/chatconsumer.cpp
new file mode 100644
index 0000000000..65840accae
--- /dev/null
+++ b/examples/network/multistreamclient/chatconsumer.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include "chatconsumer.h"
+#include <QWidget>
+#include <QTextEdit>
+#include <QLineEdit>
+#include <QVBoxLayout>
+#include <QString>
+
+ChatConsumer::ChatConsumer(QObject *parent)
+ : Consumer(parent)
+{
+ frameWidget = new QWidget;
+ frameWidget->setFocusPolicy(Qt::TabFocus);
+
+ textEdit = new QTextEdit;
+ textEdit->setFocusPolicy(Qt::NoFocus);
+ textEdit->setReadOnly(true);
+
+ lineEdit = new QLineEdit;
+ frameWidget->setFocusProxy(lineEdit);
+
+ connect(lineEdit, &QLineEdit::returnPressed, this, &ChatConsumer::returnPressed);
+
+ QVBoxLayout *layout = new QVBoxLayout(frameWidget);
+ layout->setContentsMargins( 0, 0, 0, 0);
+ layout->addWidget(textEdit);
+ layout->addWidget(lineEdit);
+}
+
+QWidget *ChatConsumer::widget()
+{
+ return frameWidget;
+}
+
+void ChatConsumer::readDatagram(const QByteArray &ba)
+{
+ textEdit->append(QString::fromUtf8(ba));
+}
+
+void ChatConsumer::returnPressed()
+{
+ emit writeDatagram(lineEdit->text().toUtf8());
+ lineEdit->clear();
+}
diff --git a/examples/network/multistreamclient/chatconsumer.h b/examples/network/multistreamclient/chatconsumer.h
new file mode 100644
index 0000000000..cac846757f
--- /dev/null
+++ b/examples/network/multistreamclient/chatconsumer.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#ifndef CHATCONSUMER_H
+#define CHATCONSUMER_H
+
+#include "consumer.h"
+
+QT_BEGIN_NAMESPACE
+class QTextEdit;
+class QLineEdit;
+QT_END_NAMESPACE
+
+class ChatConsumer : public Consumer
+{
+ Q_OBJECT
+public:
+ explicit ChatConsumer(QObject *parent = nullptr);
+
+ QWidget *widget() Q_DECL_OVERRIDE;
+ void readDatagram(const QByteArray &ba) Q_DECL_OVERRIDE;
+
+private slots:
+ void returnPressed();
+
+private:
+ QWidget *frameWidget;
+ QTextEdit *textEdit;
+ QLineEdit *lineEdit;
+};
+
+#endif
diff --git a/examples/network/multistreamclient/client.cpp b/examples/network/multistreamclient/client.cpp
new file mode 100644
index 0000000000..fbe138162e
--- /dev/null
+++ b/examples/network/multistreamclient/client.cpp
@@ -0,0 +1,207 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include <QtWidgets>
+#include <QtNetwork>
+
+#include "client.h"
+#include "movieconsumer.h"
+#include "timeconsumer.h"
+#include "chatconsumer.h"
+
+Client::Client(QWidget *parent)
+ : QDialog(parent)
+ , consumers(NumberOfChannels)
+{
+ setWindowTitle(tr("Multi-stream Client"));
+
+ sctpSocket = new QSctpSocket(this);
+
+ QLabel *hostLabel = new QLabel(tr("&Server name:"));
+ QLabel *portLabel = new QLabel(tr("S&erver port:"));
+
+ hostCombo = new QComboBox;
+ hostCombo->setEditable(true);
+ // find out name of this machine
+ QString name = QHostInfo::localHostName();
+ if (!name.isEmpty()) {
+ hostCombo->addItem(name);
+ QString domain = QHostInfo::localDomainName();
+ if (!domain.isEmpty())
+ hostCombo->addItem(name + QChar('.') + domain);
+ }
+ if (name != QString("localhost"))
+ hostCombo->addItem(QString("localhost"));
+ // find out IP addresses of this machine
+ QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
+ // add non-localhost addresses
+ for (int i = 0; i < ipAddressesList.size(); ++i) {
+ if (!ipAddressesList.at(i).isLoopback())
+ hostCombo->addItem(ipAddressesList.at(i).toString());
+ }
+ // add localhost addresses
+ for (int i = 0; i < ipAddressesList.size(); ++i) {
+ if (ipAddressesList.at(i).isLoopback())
+ hostCombo->addItem(ipAddressesList.at(i).toString());
+ }
+
+ portLineEdit = new QLineEdit;
+ portLineEdit->setValidator(new QIntValidator(1, 65535, this));
+
+ hostLabel->setBuddy(hostCombo);
+ portLabel->setBuddy(portLineEdit);
+
+ connectButton = new QPushButton(tr("Connect"));
+ connectButton->setDefault(true);
+ connectButton->setEnabled(false);
+
+ QPushButton *quitButton = new QPushButton(tr("Quit"));
+ quitButton->setAutoDefault(false);
+
+ QDialogButtonBox *buttonBox = new QDialogButtonBox;
+ buttonBox->addButton(connectButton, QDialogButtonBox::ActionRole);
+ buttonBox->addButton(quitButton, QDialogButtonBox::AcceptRole);
+
+ QLabel *movieLabel = new QLabel(tr("Movie stream:"));
+ consumers[Movie] = new MovieConsumer(this);
+ QLabel *timeLabel = new QLabel(tr("Time stream:"));
+ consumers[Time] = new TimeConsumer(this);
+ QLabel *chatLabel = new QLabel(tr("&Chat:"));
+ consumers[Chat] = new ChatConsumer(this);
+ chatLabel->setBuddy(consumers[Chat]->widget());
+
+ connect(hostCombo, &QComboBox::editTextChanged, this, &Client::enableConnectButton);
+ connect(portLineEdit, &QLineEdit::textChanged, this, &Client::enableConnectButton);
+ connect(connectButton, &QPushButton::clicked, this, &Client::requestConnect);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &Client::accept);
+ connect(sctpSocket, &QSctpSocket::connected, this, &Client::connected);
+ connect(sctpSocket, &QSctpSocket::disconnected, this, &Client::disconnected);
+ connect(sctpSocket, &QSctpSocket::channelReadyRead, this, &Client::readDatagram);
+ connect(sctpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(displayError(QAbstractSocket::SocketError)));
+ connect(consumers[Time], &Consumer::writeDatagram, this, &Client::writeDatagram);
+ connect(consumers[Chat], &Consumer::writeDatagram, this, &Client::writeDatagram);
+
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->addWidget(hostLabel, 0, 0);
+ mainLayout->addWidget(hostCombo, 0, 1);
+ mainLayout->addWidget(portLabel, 1, 0);
+ mainLayout->addWidget(portLineEdit, 1, 1);
+ mainLayout->addWidget(buttonBox, 2, 0, 1, 2);
+ mainLayout->addWidget(movieLabel, 3, 0);
+ mainLayout->addWidget(timeLabel, 3, 1);
+ mainLayout->addWidget(consumers[Movie]->widget(), 4, 0);
+ mainLayout->addWidget(consumers[Time]->widget(), 4, 1);
+ mainLayout->addWidget(chatLabel, 5, 0);
+ mainLayout->addWidget(consumers[Chat]->widget(), 6, 0, 1, 2);
+ setLayout(mainLayout);
+
+ portLineEdit->setFocus();
+}
+
+Client::~Client()
+{
+ delete sctpSocket;
+}
+
+void Client::connected()
+{
+ consumers[Chat]->widget()->setFocus();
+}
+
+void Client::disconnected()
+{
+ for (Consumer *consumer : consumers)
+ consumer->serverDisconnected();
+
+ sctpSocket->close();
+}
+
+void Client::requestConnect()
+{
+ connectButton->setEnabled(false);
+ sctpSocket->abort();
+ sctpSocket->connectToHost(hostCombo->currentText(),
+ portLineEdit->text().toInt());
+}
+
+void Client::readDatagram(int channel)
+{
+ sctpSocket->setCurrentReadChannel(channel);
+ consumers[channel]->readDatagram(sctpSocket->readDatagram().data());
+}
+
+void Client::displayError(QAbstractSocket::SocketError socketError)
+{
+ switch (socketError) {
+ case QAbstractSocket::HostNotFoundError:
+ QMessageBox::information(this, tr("Multi-stream Client"),
+ tr("The host was not found. Please check the "
+ "host name and port settings."));
+ break;
+ case QAbstractSocket::ConnectionRefusedError:
+ QMessageBox::information(this, tr("Multi-stream Client"),
+ tr("The connection was refused by the peer. "
+ "Make sure the multi-stream server is running, "
+ "and check that the host name and port "
+ "settings are correct."));
+ break;
+ default:
+ QMessageBox::information(this, tr("Multi-stream Client"),
+ tr("The following error occurred: %1.")
+ .arg(sctpSocket->errorString()));
+ }
+
+ connectButton->setEnabled(true);
+}
+
+void Client::enableConnectButton()
+{
+ connectButton->setEnabled(!hostCombo->currentText().isEmpty() &&
+ !portLineEdit->text().isEmpty());
+}
+
+void Client::writeDatagram(const QByteArray &ba)
+{
+ if (sctpSocket->isValid() && sctpSocket->state() == QAbstractSocket::ConnectedState) {
+ sctpSocket->setCurrentWriteChannel(consumers.indexOf(static_cast<Consumer *>(sender())));
+ sctpSocket->writeDatagram(ba);
+ }
+}
diff --git a/examples/network/multistreamclient/client.h b/examples/network/multistreamclient/client.h
new file mode 100644
index 0000000000..6ec804e9be
--- /dev/null
+++ b/examples/network/multistreamclient/client.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include <QDialog>
+#include <QVector>
+#include <QSctpSocket>
+
+QT_BEGIN_NAMESPACE
+class QComboBox;
+class QLineEdit;
+class QPushButton;
+class QByteArray;
+QT_END_NAMESPACE
+
+class Consumer;
+
+class Client : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit Client(QWidget *parent = nullptr);
+ virtual ~Client();
+
+private slots:
+ void connected();
+ void disconnected();
+ void requestConnect();
+ void readDatagram(int channel);
+ void displayError(QAbstractSocket::SocketError socketError);
+ void enableConnectButton();
+ void writeDatagram(const QByteArray &ba);
+
+private:
+ enum ChannelNumber {
+ Movie = 0,
+ Time = 1,
+ Chat = 2,
+
+ NumberOfChannels = 3
+ };
+
+ QVector<Consumer *> consumers;
+ QSctpSocket *sctpSocket;
+
+ QComboBox *hostCombo;
+ QLineEdit *portLineEdit;
+ QPushButton *connectButton;
+};
+
+#endif
diff --git a/examples/network/multistreamclient/consumer.h b/examples/network/multistreamclient/consumer.h
new file mode 100644
index 0000000000..0c7c1bbfb6
--- /dev/null
+++ b/examples/network/multistreamclient/consumer.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#ifndef CONSUMER_H
+#define CONSUMER_H
+
+#include <QObject>
+#include <QByteArray>
+
+QT_BEGIN_NAMESPACE
+class QWidget;
+QT_END_NAMESPACE
+
+class Consumer : public QObject
+{
+ Q_OBJECT
+public:
+ explicit inline Consumer(QObject *parent = nullptr) : QObject(parent) { }
+
+ virtual QWidget *widget() = 0;
+ virtual void readDatagram(const QByteArray &ba) = 0;
+ virtual void serverDisconnected() { }
+
+signals:
+ void writeDatagram(const QByteArray &ba);
+};
+
+#endif
diff --git a/examples/network/multistreamclient/main.cpp b/examples/network/multistreamclient/main.cpp
new file mode 100644
index 0000000000..c9e0c38d9a
--- /dev/null
+++ b/examples/network/multistreamclient/main.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include "client.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ Client client;
+
+ return (client.exec() == QDialog::Accepted) ? 0 : -1;
+}
diff --git a/examples/network/multistreamclient/movieconsumer.cpp b/examples/network/multistreamclient/movieconsumer.cpp
new file mode 100644
index 0000000000..68c7b4a229
--- /dev/null
+++ b/examples/network/multistreamclient/movieconsumer.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include "movieconsumer.h"
+#include <QLabel>
+#include <QDataStream>
+#include <QImage>
+#include <QPixmap>
+
+MovieConsumer::MovieConsumer(QObject *parent)
+ : Consumer(parent)
+{
+ label = new QLabel;
+ label->setFrameStyle(QFrame::Box | QFrame::Raised);
+ label->setFixedSize(128 + label->frameWidth() * 2,
+ 64 + label->frameWidth() * 2);
+}
+
+QWidget *MovieConsumer::widget()
+{
+ return label;
+}
+
+void MovieConsumer::readDatagram(const QByteArray &ba)
+{
+ QDataStream ds(ba);
+ QImage image;
+
+ ds >> image;
+ label->setPixmap(QPixmap::fromImage(image));
+}
+
+void MovieConsumer::serverDisconnected()
+{
+ label->setPixmap(QPixmap());
+}
diff --git a/examples/network/multistreamclient/movieconsumer.h b/examples/network/multistreamclient/movieconsumer.h
new file mode 100644
index 0000000000..0f4fd7af42
--- /dev/null
+++ b/examples/network/multistreamclient/movieconsumer.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#ifndef MOVIECONSUMER_H
+#define MOVIECONSUMER_H
+
+#include "consumer.h"
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+QT_END_NAMESPACE
+
+class MovieConsumer : public Consumer
+{
+ Q_OBJECT
+public:
+ explicit MovieConsumer(QObject *parent = nullptr);
+
+ QWidget *widget() Q_DECL_OVERRIDE;
+ void readDatagram(const QByteArray &ba) Q_DECL_OVERRIDE;
+ void serverDisconnected() Q_DECL_OVERRIDE;
+
+private:
+ QLabel *label;
+};
+
+#endif
diff --git a/examples/network/multistreamclient/multistreamclient.pro b/examples/network/multistreamclient/multistreamclient.pro
new file mode 100644
index 0000000000..ed887ab53e
--- /dev/null
+++ b/examples/network/multistreamclient/multistreamclient.pro
@@ -0,0 +1,16 @@
+QT += network widgets
+
+HEADERS = client.h \
+ consumer.h \
+ movieconsumer.h \
+ timeconsumer.h \
+ chatconsumer.h
+SOURCES = client.cpp \
+ movieconsumer.cpp \
+ timeconsumer.cpp \
+ chatconsumer.cpp \
+ main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/network/multistreamclient
+INSTALLS += target
diff --git a/examples/network/multistreamclient/timeconsumer.cpp b/examples/network/multistreamclient/timeconsumer.cpp
new file mode 100644
index 0000000000..3ad73a8712
--- /dev/null
+++ b/examples/network/multistreamclient/timeconsumer.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include "timeconsumer.h"
+#include <QLCDNumber>
+#include <QString>
+#include <QDataStream>
+#include <QTimer>
+
+TimeConsumer::TimeConsumer(QObject *parent)
+ : Consumer(parent)
+{
+ lcdNumber = new QLCDNumber(8);
+
+ QTimer *timer = new QTimer(this);
+ connect(timer, &QTimer::timeout, this, &TimeConsumer::timerTick);
+ timer->start(100);
+
+ serverDisconnected();
+}
+
+QWidget *TimeConsumer::widget()
+{
+ return lcdNumber;
+}
+
+void TimeConsumer::readDatagram(const QByteArray &ba)
+{
+ QDataStream ds(ba);
+
+ ds >> lastTime;
+ lcdNumber->display(lastTime.toString("hh:mm:ss"));
+}
+
+void TimeConsumer::timerTick()
+{
+ QByteArray buf;
+ QDataStream ds(&buf, QIODevice::WriteOnly);
+
+ ds << lastTime;
+ emit writeDatagram(buf);
+}
+
+void TimeConsumer::serverDisconnected()
+{
+ lcdNumber->display(QLatin1String("--:--:--"));
+}
diff --git a/examples/network/multistreamclient/timeconsumer.h b/examples/network/multistreamclient/timeconsumer.h
new file mode 100644
index 0000000000..d382e99d86
--- /dev/null
+++ b/examples/network/multistreamclient/timeconsumer.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#ifndef TIMECONSUMER_H
+#define TIMECONSUMER_H
+
+#include "consumer.h"
+#include <QTime>
+
+QT_BEGIN_NAMESPACE
+class QLCDNumber;
+QT_END_NAMESPACE
+
+class TimeConsumer : public Consumer
+{
+ Q_OBJECT
+public:
+ explicit TimeConsumer(QObject *parent = nullptr);
+
+ QWidget *widget() Q_DECL_OVERRIDE;
+ void readDatagram(const QByteArray &ba) Q_DECL_OVERRIDE;
+ void serverDisconnected() Q_DECL_OVERRIDE;
+
+private slots:
+ void timerTick();
+
+private:
+ QTime lastTime;
+ QLCDNumber *lcdNumber;
+};
+
+#endif
diff --git a/examples/network/multistreamserver/animation.gif b/examples/network/multistreamserver/animation.gif
new file mode 100644
index 0000000000..f674369efc
--- /dev/null
+++ b/examples/network/multistreamserver/animation.gif
Binary files differ
diff --git a/examples/network/multistreamserver/chatprovider.cpp b/examples/network/multistreamserver/chatprovider.cpp
new file mode 100644
index 0000000000..9a88d491ab
--- /dev/null
+++ b/examples/network/multistreamserver/chatprovider.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include "chatprovider.h"
+#include <QString>
+#include <QSctpSocket>
+#include <QHostAddress>
+
+ChatProvider::ChatProvider(QObject *parent)
+ : Provider(parent)
+{
+}
+
+void ChatProvider::readDatagram(QSctpSocket &from, const QByteArray &ba)
+{
+ emit writeDatagram(0, QString(QLatin1String("<%1:%2> %3"))
+ .arg(from.peerAddress().toString())
+ .arg(QString::number(from.peerPort()))
+ .arg(QString::fromUtf8(ba)).toUtf8());
+}
+
+void ChatProvider::newConnection(QSctpSocket &client)
+{
+ readDatagram(client, QString(tr("has joined")).toUtf8());
+}
+
+void ChatProvider::clientDisconnected(QSctpSocket &client)
+{
+ readDatagram(client, QString(tr("has left")).toUtf8());
+}
diff --git a/examples/network/multistreamserver/chatprovider.h b/examples/network/multistreamserver/chatprovider.h
new file mode 100644
index 0000000000..912101ec34
--- /dev/null
+++ b/examples/network/multistreamserver/chatprovider.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#ifndef CHATPROVIDER_H
+#define CHATPROVIDER_H
+
+#include "provider.h"
+
+class ChatProvider : public Provider
+{
+ Q_OBJECT
+public:
+ explicit ChatProvider(QObject *parent = nullptr);
+
+ void readDatagram(QSctpSocket &from, const QByteArray &ba) Q_DECL_OVERRIDE;
+ void newConnection(QSctpSocket &client) Q_DECL_OVERRIDE;
+ void clientDisconnected(QSctpSocket &client) Q_DECL_OVERRIDE;
+};
+
+#endif
diff --git a/examples/network/multistreamserver/main.cpp b/examples/network/multistreamserver/main.cpp
new file mode 100644
index 0000000000..04350cd13d
--- /dev/null
+++ b/examples/network/multistreamserver/main.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include <QApplication>
+
+#include "server.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ Server server;
+
+ return (server.exec() == QDialog::Accepted) ? 0 : -1;
+}
diff --git a/examples/network/multistreamserver/movieprovider.cpp b/examples/network/multistreamserver/movieprovider.cpp
new file mode 100644
index 0000000000..cc534e6f40
--- /dev/null
+++ b/examples/network/multistreamserver/movieprovider.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include "movieprovider.h"
+#include <QMovie>
+#include <QString>
+#include <QDataStream>
+
+MovieProvider::MovieProvider(QObject *parent)
+ : Provider(parent)
+{
+ movie = new QMovie(this);
+ movie->setCacheMode(QMovie::CacheAll);
+ movie->setFileName(QLatin1String("animation.gif"));
+ connect(movie, &QMovie::frameChanged, this, &MovieProvider::frameChanged);
+ movie->start();
+}
+
+void MovieProvider::frameChanged()
+{
+ QByteArray buf;
+ QDataStream ds(&buf, QIODevice::WriteOnly);
+
+ ds << movie->currentImage();
+ emit writeDatagram(0, buf);
+}
diff --git a/examples/network/multistreamserver/movieprovider.h b/examples/network/multistreamserver/movieprovider.h
new file mode 100644
index 0000000000..d6d8fb7449
--- /dev/null
+++ b/examples/network/multistreamserver/movieprovider.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#ifndef MOVIEPROVIDER_H
+#define MOVIEPROVIDER_H
+
+#include "provider.h"
+
+QT_BEGIN_NAMESPACE
+class QMovie;
+QT_END_NAMESPACE
+
+class MovieProvider : public Provider
+{
+ Q_OBJECT
+public:
+ explicit MovieProvider(QObject *parent = nullptr);
+
+private slots:
+ void frameChanged();
+
+private:
+ QMovie *movie;
+};
+
+#endif
diff --git a/examples/network/multistreamserver/multistreamserver.pro b/examples/network/multistreamserver/multistreamserver.pro
new file mode 100644
index 0000000000..75a7e6bbec
--- /dev/null
+++ b/examples/network/multistreamserver/multistreamserver.pro
@@ -0,0 +1,24 @@
+QT += network widgets
+
+HEADERS = server.h \
+ provider.h \
+ movieprovider.h \
+ timeprovider.h \
+ chatprovider.h
+SOURCES = server.cpp \
+ movieprovider.cpp \
+ timeprovider.cpp \
+ chatprovider.cpp \
+ main.cpp
+
+EXAMPLE_FILES = animation.gif
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/network/multistreamserver
+INSTALLS += target
+
+wince*: {
+ addFiles.files += *.gif
+ addFiles.path = .
+ DEPLOYMENT += addFiles
+}
diff --git a/examples/network/multistreamserver/provider.h b/examples/network/multistreamserver/provider.h
new file mode 100644
index 0000000000..c6ec612275
--- /dev/null
+++ b/examples/network/multistreamserver/provider.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#ifndef PROVIDER_H
+#define PROVIDER_H
+
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+class QSctpSocket;
+class QByteArray;
+QT_END_NAMESPACE
+
+class Provider : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit inline Provider(QObject *parent = nullptr) : QObject(parent) { }
+
+ virtual void readDatagram(QSctpSocket &, const QByteArray &) { }
+ virtual void newConnection(QSctpSocket &) { }
+ virtual void clientDisconnected(QSctpSocket &) { }
+
+signals:
+ void writeDatagram(QSctpSocket *to, const QByteArray &ba);
+};
+
+#endif
diff --git a/examples/network/multistreamserver/server.cpp b/examples/network/multistreamserver/server.cpp
new file mode 100644
index 0000000000..1fb18e80b9
--- /dev/null
+++ b/examples/network/multistreamserver/server.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include <QtWidgets>
+#include <QtNetwork>
+#include <QtAlgorithms>
+
+#include "server.h"
+#include "movieprovider.h"
+#include "timeprovider.h"
+#include "chatprovider.h"
+
+Server::Server(QWidget *parent)
+ : QDialog(parent)
+ , providers(NumberOfChannels)
+{
+ setWindowTitle(tr("Multi-stream Server"));
+
+ sctpServer = new QSctpServer(this);
+ sctpServer->setMaxChannelCount(NumberOfChannels);
+
+ statusLabel = new QLabel;
+ QPushButton *quitButton = new QPushButton(tr("Quit"));
+
+ providers[Movie] = new MovieProvider(this);
+ providers[Time] = new TimeProvider(this);
+ providers[Chat] = new ChatProvider(this);
+
+ connect(sctpServer, &QSctpServer::newConnection, this, &Server::newConnection);
+ connect(quitButton, &QPushButton::clicked, this, &Server::accept);
+ connect(providers[Movie], &Provider::writeDatagram, this, &Server::writeDatagram);
+ connect(providers[Time], &Provider::writeDatagram, this, &Server::writeDatagram);
+ connect(providers[Chat], &Provider::writeDatagram, this, &Server::writeDatagram);
+
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(statusLabel);
+ mainLayout->addWidget(quitButton);
+ setLayout(mainLayout);
+}
+
+Server::~Server()
+{
+ qDeleteAll(connections.begin(), connections.end());
+}
+
+int Server::exec()
+{
+ if (!sctpServer->listen()) {
+ QMessageBox::critical(this, windowTitle(),
+ tr("Unable to start the server: %1.")
+ .arg(sctpServer->errorString()));
+ return QDialog::Rejected;
+ }
+
+ QString ipAddress;
+ QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
+ // use the first non-localhost IPv4 address
+ for (int i = 0; i < ipAddressesList.size(); ++i) {
+ if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
+ ipAddressesList.at(i).toIPv4Address()) {
+ ipAddress = ipAddressesList.at(i).toString();
+ break;
+ }
+ }
+ // if we did not find one, use IPv4 localhost
+ if (ipAddress.isEmpty())
+ ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
+ statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n"
+ "Run the Multi-stream Client example now.")
+ .arg(ipAddress).arg(sctpServer->serverPort()));
+
+ return QDialog::exec();
+}
+
+void Server::newConnection()
+{
+ QSctpSocket *connection = sctpServer->nextPendingDatagramConnection();
+
+ connections.append(connection);
+ connect(connection, &QSctpSocket::channelReadyRead, this, &Server::readDatagram);
+ connect(connection, &QSctpSocket::disconnected, this, &Server::clientDisconnected);
+
+ for (Provider *provider : providers)
+ provider->newConnection(*connection);
+}
+
+void Server::clientDisconnected()
+{
+ QSctpSocket *connection = static_cast<QSctpSocket *>(sender());
+
+ connections.removeOne(connection);
+ connection->disconnect();
+
+ for (Provider *provider : providers)
+ provider->clientDisconnected(*connection);
+
+ connection->deleteLater();
+}
+
+void Server::readDatagram(int channel)
+{
+ QSctpSocket *connection = static_cast<QSctpSocket *>(sender());
+
+ connection->setCurrentReadChannel(channel);
+ providers[channel]->readDatagram(*connection, connection->readDatagram().data());
+}
+
+void Server::writeDatagram(QSctpSocket *to, const QByteArray &ba)
+{
+ int channel = providers.indexOf(static_cast<Provider *>(sender()));
+
+ if (to) {
+ to->setCurrentWriteChannel(channel);
+ to->writeDatagram(ba);
+ return;
+ }
+
+ for (QSctpSocket *connection : connections) {
+ connection->setCurrentWriteChannel(channel);
+ connection->writeDatagram(ba);
+ }
+}
diff --git a/examples/network/multistreamserver/server.h b/examples/network/multistreamserver/server.h
new file mode 100644
index 0000000000..6d01f7a238
--- /dev/null
+++ b/examples/network/multistreamserver/server.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#ifndef SERVER_H
+#define SERVER_H
+
+#include <QDialog>
+#include <QVector>
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+class QSctpServer;
+class QSctpSocket;
+class QLabel;
+class QByteArray;
+QT_END_NAMESPACE
+
+class Provider;
+
+class Server : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit Server(QWidget *parent = nullptr);
+ virtual ~Server();
+
+public slots:
+ int exec() Q_DECL_OVERRIDE;
+
+private slots:
+ void newConnection();
+ void clientDisconnected();
+ void readDatagram(int channel);
+ void writeDatagram(QSctpSocket *to, const QByteArray &ba);
+
+private:
+ enum ChannelNumber {
+ Movie = 0,
+ Time = 1,
+ Chat = 2,
+
+ NumberOfChannels = 3
+ };
+
+ QVector<Provider *> providers;
+ QSctpServer *sctpServer;
+ QList<QSctpSocket *> connections;
+
+ QLabel *statusLabel;
+};
+
+#endif
diff --git a/examples/network/multistreamserver/timeprovider.cpp b/examples/network/multistreamserver/timeprovider.cpp
new file mode 100644
index 0000000000..043810cdf3
--- /dev/null
+++ b/examples/network/multistreamserver/timeprovider.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include "timeprovider.h"
+#include <QDataStream>
+#include <QTime>
+#include <QByteArray>
+
+TimeProvider::TimeProvider(QObject *parent)
+ : Provider(parent)
+{
+}
+
+void TimeProvider::readDatagram(QSctpSocket &from, const QByteArray &ba)
+{
+ QDataStream in_ds(ba);
+ QTime curTime = QTime::currentTime();
+ QTime clientTime;
+
+ in_ds >> clientTime;
+ if (!clientTime.isValid() || curTime.second() != clientTime.second()
+ || curTime.minute() != clientTime.minute()
+ || curTime.hour() != clientTime.hour()) {
+ QByteArray buf;
+ QDataStream out_ds(&buf, QIODevice::WriteOnly);
+
+ out_ds << curTime;
+ emit writeDatagram(&from, buf);
+ }
+}
diff --git a/examples/network/multistreamserver/timeprovider.h b/examples/network/multistreamserver/timeprovider.h
new file mode 100644
index 0000000000..b57c066c24
--- /dev/null
+++ b/examples/network/multistreamserver/timeprovider.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#ifndef TIMEPROVIDER_H
+#define TIMEPROVIDER_H
+
+#include "provider.h"
+
+class TimeProvider : public Provider
+{
+ Q_OBJECT
+public:
+ explicit TimeProvider(QObject *parent = nullptr);
+
+ void readDatagram(QSctpSocket &from, const QByteArray &ba) Q_DECL_OVERRIDE;
+};
+
+#endif
diff --git a/examples/network/network.pro b/examples/network/network.pro
index 86bb08d22a..ff7fbcf4f2 100644
--- a/examples/network/network.pro
+++ b/examples/network/network.pro
@@ -32,4 +32,5 @@ qtHaveModule(widgets) {
contains(QT_CONFIG, openssl):SUBDIRS += securesocketclient
contains(QT_CONFIG, openssl-linked):SUBDIRS += securesocketclient
+ contains(QT_CONFIG, sctp):SUBDIRS += multistreamserver multistreamclient
}
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
diff --git a/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp b/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp
index d159d6d683..bc9d3cc9bf 100644
--- a/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp
+++ b/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp
@@ -603,8 +603,8 @@ void tst_PlatformSocketEngine::invalidSend()
PLATFORMSOCKETENGINE socket;
QVERIFY(socket.initialize(QAbstractSocket::TcpSocket));
- QTest::ignoreMessage(QtWarningMsg, PLATFORMSOCKETENGINESTRING "::writeDatagram() was"
- " called by a socket other than QAbstractSocket::UdpSocket");
+ QTest::ignoreMessage(QtWarningMsg, PLATFORMSOCKETENGINESTRING "::writeDatagram() was called"
+ " not in QAbstractSocket::BoundState or QAbstractSocket::ConnectedState");
QCOMPARE(socket.writeDatagram("hei", 3, QIpPacketHeader(QHostAddress::LocalHost, 143)),
(qlonglong) -1);
}
diff --git a/tests/auto/network/socket/qsctpsocket/qsctpsocket.pro b/tests/auto/network/socket/qsctpsocket/qsctpsocket.pro
new file mode 100644
index 0000000000..49a40ce9b5
--- /dev/null
+++ b/tests/auto/network/socket/qsctpsocket/qsctpsocket.pro
@@ -0,0 +1,6 @@
+CONFIG += testcase
+TARGET = tst_qsctpsocket
+QT = core network testlib
+
+SOURCES += tst_qsctpsocket.cpp
+
diff --git a/tests/auto/network/socket/qsctpsocket/tst_qsctpsocket.cpp b/tests/auto/network/socket/qsctpsocket/tst_qsctpsocket.cpp
new file mode 100644
index 0000000000..70297f8cc4
--- /dev/null
+++ b/tests/auto/network/socket/qsctpsocket/tst_qsctpsocket.cpp
@@ -0,0 +1,489 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QDebug>
+#include <QEventLoop>
+#include <QByteArray>
+#include <QString>
+#include <QHostAddress>
+#include <QHostInfo>
+#include <QNetworkInterface>
+#include <QTime>
+
+#include <QSctpSocket>
+#include <QSctpServer>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/sctp.h>
+
+#define SOCKET int
+#define INVALID_SOCKET -1
+
+class tst_QSctpSocket : public QObject
+{
+ Q_OBJECT
+
+public:
+ static void enterLoop(int secs)
+ {
+ ++loopLevel;
+ QTestEventLoop::instance().enterLoop(secs);
+ --loopLevel;
+ }
+ static void exitLoop()
+ {
+ // Safe exit - if we aren't in an event loop, don't
+ // exit one.
+ if (loopLevel > 0)
+ QTestEventLoop::instance().exitLoop();
+ }
+ static bool timeout()
+ {
+ return QTestEventLoop::instance().timeout();
+ }
+
+private slots:
+ void constructing();
+ void bind_data();
+ void bind();
+ void setInvalidSocketDescriptor();
+ void setSocketDescriptor();
+ void socketDescriptor();
+ void hostNotFound();
+ void connecting();
+ void readAndWrite();
+ void loop_data();
+ void loop();
+ void loopInTCPMode_data();
+ void loopInTCPMode();
+ void readDatagramAfterClose();
+ void clientSendDataOnDelayedDisconnect();
+
+protected slots:
+ void exitLoopSlot();
+
+private:
+ static int loopLevel;
+};
+
+int tst_QSctpSocket::loopLevel = 0;
+
+//----------------------------------------------------------------------------------
+void tst_QSctpSocket::constructing()
+{
+ QSctpSocket socket;
+
+ // Check the initial state of the QSctpSocket.
+ QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
+ QVERIFY(socket.isSequential());
+ QVERIFY(!socket.isOpen());
+ QVERIFY(!socket.isValid());
+ QCOMPARE(socket.socketType(), QAbstractSocket::SctpSocket);
+ QCOMPARE(socket.maxChannelCount(), 0);
+ QCOMPARE(socket.readChannelCount(), 0);
+ QCOMPARE(socket.writeChannelCount(), 0);
+
+ char c;
+ QCOMPARE(socket.getChar(&c), false);
+ QCOMPARE((int) socket.bytesAvailable(), 0);
+ QCOMPARE(socket.canReadLine(), false);
+ QCOMPARE(socket.readLine(), QByteArray());
+ QCOMPARE(socket.socketDescriptor(), (qintptr)-1);
+ QCOMPARE((int) socket.localPort(), 0);
+ QVERIFY(socket.localAddress() == QHostAddress());
+ QCOMPARE((int) socket.peerPort(), 0);
+ QVERIFY(socket.peerAddress() == QHostAddress());
+ QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError);
+ QCOMPARE(socket.errorString(), QString("Unknown error"));
+}
+
+//----------------------------------------------------------------------------------
+void tst_QSctpSocket::bind_data()
+{
+ QTest::addColumn<QString>("stringAddr");
+ QTest::addColumn<bool>("successExpected");
+ QTest::addColumn<QString>("stringExpectedLocalAddress");
+
+ // iterate all interfaces, add all addresses on them as test data
+ QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
+ foreach (const QNetworkInterface &interface, interfaces) {
+ if (!interface.isValid())
+ continue;
+
+ foreach (const QNetworkAddressEntry &entry, interface.addressEntries()) {
+ if (entry.ip().isInSubnet(QHostAddress::parseSubnet("fe80::/10"))
+ || entry.ip().isInSubnet(QHostAddress::parseSubnet("169.254/16")))
+ continue; // link-local bind will fail, at least on Linux, so skip it.
+
+ QString ip(entry.ip().toString());
+ QTest::newRow(ip.toLatin1().constData()) << ip << true << ip;
+ }
+ }
+
+ // additionally, try bind to known-bad addresses, and make sure this doesn't work
+ // these ranges are guaranteed to be reserved for 'documentation purposes',
+ // and thus, should be unused in the real world. Not that I'm assuming the
+ // world is full of competent administrators, or anything.
+ QStringList knownBad;
+ knownBad << "198.51.100.1";
+ knownBad << "2001:0DB8::1";
+ foreach (const QString &badAddress, knownBad) {
+ QTest::newRow(badAddress.toLatin1().constData()) << badAddress << false << QString();
+ }
+}
+
+// Testing bind function
+void tst_QSctpSocket::bind()
+{
+ QFETCH(QString, stringAddr);
+ QFETCH(bool, successExpected);
+ QFETCH(QString, stringExpectedLocalAddress);
+
+ QHostAddress addr(stringAddr);
+ QHostAddress expectedLocalAddress(stringExpectedLocalAddress);
+
+ QSctpSocket socket;
+ qDebug() << "Binding " << addr;
+
+ if (successExpected) {
+ QVERIFY2(socket.bind(addr), qPrintable(socket.errorString()));
+ } else {
+ QVERIFY(!socket.bind(addr));
+ }
+
+ QCOMPARE(socket.localAddress(), expectedLocalAddress);
+}
+
+//----------------------------------------------------------------------------------
+void tst_QSctpSocket::setInvalidSocketDescriptor()
+{
+ QSctpSocket socket;
+ QCOMPARE(socket.socketDescriptor(), (qintptr)INVALID_SOCKET);
+ QVERIFY(!socket.setSocketDescriptor(-5, QAbstractSocket::UnconnectedState));
+ QCOMPARE(socket.socketDescriptor(), (qintptr)INVALID_SOCKET);
+
+ QCOMPARE(socket.error(), QAbstractSocket::UnsupportedSocketOperationError);
+}
+
+//----------------------------------------------------------------------------------
+void tst_QSctpSocket::setSocketDescriptor()
+{
+ QSctpServer server;
+
+ server.setMaxChannelCount(16);
+ QVERIFY(server.listen());
+
+ SOCKET sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
+
+ QVERIFY(sock != INVALID_SOCKET);
+ QSctpSocket socket;
+ QVERIFY(socket.setSocketDescriptor(sock, QAbstractSocket::UnconnectedState));
+ QCOMPARE(socket.socketDescriptor(), (qintptr)sock);
+ QCOMPARE(socket.readChannelCount(), 0);
+ QCOMPARE(socket.writeChannelCount(), 0);
+
+ socket.connectToHost(QHostAddress::LocalHost, server.serverPort());
+ QVERIFY(socket.waitForConnected(3000));
+ QVERIFY(server.waitForNewConnection(3000));
+
+ QCOMPARE(socket.readChannelCount(), server.maxChannelCount());
+ QVERIFY(socket.writeChannelCount() <= server.maxChannelCount());
+
+ QSctpSocket *acceptedSocket = server.nextPendingDatagramConnection();
+ QVERIFY(acceptedSocket);
+ QCOMPARE(acceptedSocket->readChannelCount(), socket.writeChannelCount());
+ QCOMPARE(acceptedSocket->writeChannelCount(), socket.readChannelCount());
+}
+
+//----------------------------------------------------------------------------------
+void tst_QSctpSocket::socketDescriptor()
+{
+ QSctpSocket socket;
+
+ QSctpServer server;
+
+ QVERIFY(server.listen());
+
+ QCOMPARE(socket.socketDescriptor(), (qintptr)INVALID_SOCKET);
+ socket.connectToHost(QHostAddress::LocalHost, server.serverPort());
+ QVERIFY(server.waitForNewConnection(3000));
+ if (socket.state() != QAbstractSocket::ConnectedState) {
+ QVERIFY((socket.state() == QAbstractSocket::HostLookupState
+ && socket.socketDescriptor() == INVALID_SOCKET)
+ || socket.state() == QAbstractSocket::ConnectingState);
+ QVERIFY(socket.waitForConnected(3000));
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ }
+ QVERIFY(socket.socketDescriptor() != INVALID_SOCKET);
+}
+
+//----------------------------------------------------------------------------------
+void tst_QSctpSocket::hostNotFound()
+{
+ QSctpSocket socket;
+
+ socket.connectToHost("nosuchserver.qt-project.org", 80);
+ QVERIFY(!socket.waitForConnected(3000));
+ QCOMPARE(socket.state(), QTcpSocket::UnconnectedState);
+ QCOMPARE(socket.error(), QAbstractSocket::HostNotFoundError);
+}
+
+// Testing connect function
+void tst_QSctpSocket::connecting()
+{
+ QSctpServer server;
+
+ QVERIFY(server.listen());
+
+ QSctpSocket socket;
+ socket.connectToHost(QHostAddress::LocalHost, server.serverPort());
+ QVERIFY(socket.waitForConnected(3000));
+
+ QVERIFY(server.waitForNewConnection(3000));
+ QSctpSocket *acceptedSocket = server.nextPendingDatagramConnection();
+ QVERIFY(acceptedSocket);
+
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(acceptedSocket->state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(socket.readChannelCount(), acceptedSocket->readChannelCount());
+ QCOMPARE(socket.writeChannelCount(),acceptedSocket->writeChannelCount());
+}
+
+// Testing read/write functions
+void tst_QSctpSocket::readAndWrite()
+{
+ QSctpServer server;
+
+ QVERIFY(server.listen());
+
+ QSctpSocket socket;
+ socket.connectToHost(QHostAddress::LocalHost, server.serverPort());
+ QVERIFY(socket.waitForConnected(3000));
+
+ QVERIFY(server.waitForNewConnection(3000));
+ QSctpSocket *acceptedSocket = server.nextPendingDatagramConnection();
+ QVERIFY(acceptedSocket);
+
+ QByteArray ba(1000, 1);
+ QVERIFY(acceptedSocket->writeDatagram(ba));
+ QVERIFY(acceptedSocket->waitForBytesWritten(3000));
+
+ QVERIFY(socket.waitForReadyRead(3000));
+ QNetworkDatagram datagram = socket.readDatagram();
+ QVERIFY(datagram.isValid());
+ QCOMPARE(datagram.data(), ba);
+
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError);
+ QCOMPARE(socket.errorString(), QString("Unknown error"));
+ QCOMPARE(acceptedSocket->state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(acceptedSocket->error(), QAbstractSocket::UnknownSocketError);
+ QCOMPARE(acceptedSocket->errorString(), QString("Unknown error"));
+}
+
+//----------------------------------------------------------------------------------
+void tst_QSctpSocket::loop_data()
+{
+ QTest::addColumn<QByteArray>("peterDatagram");
+ QTest::addColumn<QByteArray>("paulDatagram");
+ QTest::addColumn<int>("peterChannel");
+ QTest::addColumn<int>("paulChannel");
+
+ QTest::newRow("\"Almond!\" | \"Joy!\"") << QByteArray("Almond!") << QByteArray("Joy!") << 0 << 0;
+ QTest::newRow("\"A\" | \"B\"") << QByteArray("A") << QByteArray("B") << 1 << 1;
+ QTest::newRow("\"AB\" | \"B\"") << QByteArray("AB") << QByteArray("B") << 0 << 1;
+ QTest::newRow("\"AB\" | \"BB\"") << QByteArray("AB") << QByteArray("BB") << 1 << 0;
+ QTest::newRow("\"A\\0B\" | \"B\\0B\"") << QByteArray::fromRawData("A\0B", 3) << QByteArray::fromRawData("B\0B", 3) << 0 << 1;
+ QTest::newRow("BigDatagram") << QByteArray(600, '@') << QByteArray(600, '@') << 1 << 0;
+}
+
+void tst_QSctpSocket::loop()
+{
+ QFETCH(QByteArray, peterDatagram);
+ QFETCH(QByteArray, paulDatagram);
+ QFETCH(int, peterChannel);
+ QFETCH(int, paulChannel);
+
+ QSctpServer server;
+
+ server.setMaxChannelCount(10);
+ QVERIFY(server.listen());
+
+ QSctpSocket peter;
+ peter.setMaxChannelCount(10);
+ peter.connectToHost(QHostAddress::LocalHost, server.serverPort());
+ QVERIFY(peter.waitForConnected(3000));
+
+ QVERIFY(server.waitForNewConnection(3000));
+ QSctpSocket *paul = server.nextPendingDatagramConnection();
+ QVERIFY(paul);
+
+ peter.setCurrentWriteChannel(peterChannel);
+ QVERIFY(peter.writeDatagram(peterDatagram));
+ paul->setCurrentWriteChannel(paulChannel);
+ QVERIFY(paul->writeDatagram(paulDatagram));
+ QVERIFY(peter.flush());
+ QVERIFY(paul->flush());
+
+ peter.setCurrentReadChannel(paulChannel);
+ QVERIFY(peter.waitForReadyRead(3000));
+ QCOMPARE(peter.bytesAvailable(), paulDatagram.size());
+ QCOMPARE(peter.readDatagram().data(), paulDatagram);
+
+ paul->setCurrentReadChannel(peterChannel);
+ QVERIFY(paul->waitForReadyRead(3000));
+ QCOMPARE(paul->bytesAvailable(), peterDatagram.size());
+ QCOMPARE(paul->readDatagram().data(), peterDatagram);
+}
+
+//----------------------------------------------------------------------------------
+void tst_QSctpSocket::loopInTCPMode_data()
+{
+ QTest::addColumn<QByteArray>("peterDatagram");
+ QTest::addColumn<QByteArray>("paulDatagram");
+
+ QTest::newRow("\"Almond!\" | \"Joy!\"") << QByteArray("Almond!") << QByteArray("Joy!");
+ QTest::newRow("\"A\" | \"B\"") << QByteArray("A") << QByteArray("B");
+ QTest::newRow("\"AB\" | \"B\"") << QByteArray("AB") << QByteArray("B");
+ QTest::newRow("\"AB\" | \"BB\"") << QByteArray("AB") << QByteArray("BB");
+ QTest::newRow("\"A\\0B\" | \"B\\0B\"") << QByteArray::fromRawData("A\0B", 3) << QByteArray::fromRawData("B\0B", 3);
+ QTest::newRow("BigDatagram") << QByteArray(600, '@') << QByteArray(600, '@');
+}
+
+void tst_QSctpSocket::loopInTCPMode()
+{
+ QFETCH(QByteArray, peterDatagram);
+ QFETCH(QByteArray, paulDatagram);
+
+ QSctpServer server;
+
+ server.setMaxChannelCount(-1);
+ QVERIFY(server.listen());
+
+ QSctpSocket peter;
+ peter.setMaxChannelCount(-1);
+ peter.connectToHost(QHostAddress::LocalHost, server.serverPort());
+ QVERIFY(peter.waitForConnected(3000));
+ QVERIFY(server.waitForNewConnection(3000));
+
+ QTcpSocket *paul = server.nextPendingConnection();
+ QVERIFY(paul);
+
+ QCOMPARE(peter.write(peterDatagram), qint64(peterDatagram.size()));
+ QCOMPARE(paul->write(paulDatagram), qint64(paulDatagram.size()));
+ QVERIFY(peter.flush());
+ QVERIFY(paul->flush());
+
+ QVERIFY(peter.waitForReadyRead(3000));
+ QVERIFY(paul->waitForReadyRead(3000));
+
+ QCOMPARE(peter.bytesAvailable(), paulDatagram.size());
+ QByteArray peterBuffer = peter.readAll();
+
+ QCOMPARE(paul->bytesAvailable(), peterDatagram.size());
+ QByteArray paulBuffer = paul->readAll();
+
+ QCOMPARE(peterBuffer, paulDatagram);
+ QCOMPARE(paulBuffer, peterDatagram);
+}
+
+//----------------------------------------------------------------------------------
+void tst_QSctpSocket::exitLoopSlot()
+{
+ exitLoop();
+}
+
+//----------------------------------------------------------------------------------
+void tst_QSctpSocket::readDatagramAfterClose()
+{
+ QSctpServer server;
+
+ QVERIFY(server.listen());
+
+ QSctpSocket socket;
+ socket.connectToHost(QHostAddress::LocalHost, server.serverPort());
+ QVERIFY(socket.waitForConnected(3000));
+ QVERIFY(server.waitForNewConnection(3000));
+
+ QSctpSocket *acceptedSocket = server.nextPendingDatagramConnection();
+ QVERIFY(acceptedSocket);
+
+ connect(&socket, &QIODevice::readyRead, this, &tst_QSctpSocket::exitLoopSlot);
+
+ QByteArray ba(1000, 1);
+ QVERIFY(acceptedSocket->writeDatagram(ba));
+
+ enterLoop(10);
+ if (timeout())
+ QFAIL("Network operation timed out");
+
+ QCOMPARE(socket.bytesAvailable(), ba.size());
+ socket.close();
+ QVERIFY(!socket.readDatagram().isValid());
+}
+
+// Test buffered socket properly send data on delayed disconnect
+void tst_QSctpSocket::clientSendDataOnDelayedDisconnect()
+{
+ QSctpServer server;
+
+ QVERIFY(server.listen());
+
+ // Connect to server, write data and close socket
+ QSctpSocket socket;
+ socket.connectToHost(QHostAddress::LocalHost, server.serverPort());
+ QVERIFY(socket.waitForConnected(3000));
+
+ QByteArray sendData("GET /\r\n");
+ sendData = sendData.repeated(1000);
+ QVERIFY(socket.writeDatagram(sendData));
+ socket.close();
+ QCOMPARE(socket.state(), QAbstractSocket::ClosingState);
+ QVERIFY(socket.waitForDisconnected(3000));
+
+ QVERIFY(server.waitForNewConnection(3000));
+ QSctpSocket *acceptedSocket = server.nextPendingDatagramConnection();
+ QVERIFY(acceptedSocket);
+
+ QVERIFY(acceptedSocket->waitForReadyRead(3000));
+ QNetworkDatagram datagram = acceptedSocket->readDatagram();
+ QVERIFY(datagram.isValid());
+ QCOMPARE(datagram.data(), sendData);
+}
+
+QTEST_MAIN(tst_QSctpSocket)
+
+#include "tst_qsctpsocket.moc"
diff --git a/tests/auto/network/socket/socket.pro b/tests/auto/network/socket/socket.pro
index 436ebe5c7f..75f9e3f3a0 100644
--- a/tests/auto/network/socket/socket.pro
+++ b/tests/auto/network/socket/socket.pro
@@ -8,6 +8,7 @@ SUBDIRS=\
qsocks5socketengine \
qabstractsocket \
platformsocketengine \
+ qsctpsocket \
!contains(QT_CONFIG, private_tests): SUBDIRS -= \
platformsocketengine \
@@ -15,6 +16,9 @@ SUBDIRS=\
qhttpsocketengine \
qsocks5socketengine \
+!contains(QT_CONFIG, sctp): SUBDIRS -= \
+ qsctpsocket \
+
winrt: SUBDIRS -= \
qhttpsocketengine \
qsocks5socketengine \
diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp
index 312316aba3..fa282ea6e6 100644
--- a/tools/configure/configureapp.cpp
+++ b/tools/configure/configureapp.cpp
@@ -180,6 +180,7 @@ Configure::Configure(int& argc, char** argv) : verbose(0)
dictionary[ "PPS" ] = "no";
dictionary[ "LGMON" ] = "no";
dictionary[ "SYSTEM_PROXIES" ] = "yes";
+ dictionary[ "SCTP" ] = "no";
dictionary[ "WERROR" ] = "auto";
dictionary[ "QREAL" ] = "double";
dictionary[ "ATOMIC64" ] = "auto";
@@ -829,6 +830,10 @@ void Configure::parseCmdLine()
dictionary[ "SYSTEM_PROXIES" ] = "no";
} else if (configCmdLine.at(i) == "-system-proxies") {
dictionary[ "SYSTEM_PROXIES" ] = "yes";
+ } else if (configCmdLine.at(i) == "-no-sctp") {
+ dictionary[ "SCTP" ] = "no";
+ } else if (configCmdLine.at(i) == "-sctp") {
+ dictionary[ "SCTP" ] = "yes";
} else if (configCmdLine.at(i) == "-warnings-are-errors" ||
configCmdLine.at(i) == "-Werror") {
dictionary[ "WERROR" ] = "yes";
@@ -1698,6 +1703,9 @@ bool Configure::displayHelp()
desc("SYSTEM_PROXIES", "yes", "-system-proxies", "Use system network proxies by default.");
desc("SYSTEM_PROXIES", "no", "-no-system-proxies", "Do not use system network proxies by default.\n");
+ desc("SCTP", "yes", "-sctp", "Compile SCTP support.");
+ desc("SCTP", "no", "-no-sctp", "Do not compile SCTP network protocol support.\n");
+
desc("WERROR", "yes", "-warnings-are-errors", "Make warnings be treated as errors.");
desc("WERROR", "no", "-no-warnings-are-errors","Make warnings be treated normally.");
@@ -2103,6 +2111,8 @@ bool Configure::checkAvailability(const QString &part)
available = (platform() == QNX) && tryCompileProject("unix/pps");
} else if (part == "LGMON") {
available = (platform() == QNX) && tryCompileProject("unix/lgmon");
+ } else if (part == "SCTP") {
+ available = tryCompileProject("unix/sctp");
} else if (part == "NEON") {
available = dictionary["QT_CPU_FEATURES"].contains("neon");
} else if (part == "FONT_CONFIG") {
@@ -2301,6 +2311,10 @@ void Configure::autoDetection()
dictionary["LGMON"] = checkAvailability("LGMON") ? "yes" : "no";
}
+ if (dictionary["SCTP"] == "auto") {
+ dictionary["SCTP"] = checkAvailability("SCTP") ? "yes" : "no";
+ }
+
if (dictionary["QT_EVENTFD"] == "auto")
dictionary["QT_EVENTFD"] = checkAvailability("QT_EVENTFD") ? "yes" : "no";
@@ -2744,6 +2758,9 @@ void Configure::generateOutputVars()
if (dictionary[ "SYSTEM_PROXIES" ] == "yes")
qtConfig += "system-proxies";
+ if (dictionary[ "SCTP" ] == "yes")
+ qtConfig += "sctp";
+
if (dictionary.contains("XQMAKESPEC") && (dictionary["QMAKESPEC"] != dictionary["XQMAKESPEC"])) {
qmakeConfig += "cross_compile";
dictionary["CROSS_COMPILE"] = "yes";
@@ -3387,6 +3404,8 @@ void Configure::generateConfigfiles()
else
qconfigList += "QT_NO_NIS";
+ if (dictionary["SCTP"] == "no") qconfigList += "QT_NO_SCTP";
+
if (dictionary["LARGE_FILE"] == "yes") qconfigList += "QT_LARGEFILE_SUPPORT=64";
if (dictionary["QT_CUPS"] == "no") qconfigList += "QT_NO_CUPS";
if (dictionary["QT_ICONV"] == "no") qconfigList += "QT_NO_ICONV";
@@ -3520,6 +3539,7 @@ void Configure::displayConfig()
sout << "DirectWrite support........." << dictionary[ "DIRECTWRITE" ] << endl;
sout << "DirectWrite 2 support......." << dictionary[ "DIRECTWRITE2" ] << endl;
sout << "Use system proxies.........." << dictionary[ "SYSTEM_PROXIES" ] << endl;
+ sout << "SCTP support................" << dictionary[ "SCTP" ] << endl;
sout << endl;
sout << "QPA Backends:" << endl;