Introduce SCTP sockets support

Add protocol-specific code and the QSctpServer, QSctpSocket classes.

Change-Id: Ie9a1d87bd1fda866a2405043d1c15c12ded5a96e
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Alex Trotsenko 2014-09-24 17:31:33 +03:00
parent c5a4b093d0
commit 75a9bd2a4f
66 changed files with 3875 additions and 121 deletions

View File

@ -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;
}

View File

@ -0,0 +1,4 @@
SOURCES = sctp.cpp
CONFIG -= qt
QT =
LIBS += $$QMAKE_LIBS_NETWORK

View File

@ -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)

View File

@ -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"
]
},

View File

@ -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();
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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());
}

View File

@ -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

View File

@ -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

View File

@ -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("--:--:--"));
}

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -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());
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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
}

View File

@ -738,6 +738,18 @@ void QIODevicePrivate::setWriteChannelCount(int count)
setCurrentWriteChannel(currentWriteChannel);
}
/*!
\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

View File

@ -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)

View File

@ -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]

View File

@ -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;

View File

@ -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;

View File

@ -212,6 +212,12 @@
lookup on a remote host name and connect to it, as opposed to
requiring the application to perform the name lookup and request
connection to IP addresses only.
\value SctpTunnelingCapability Ability to open transparent, tunneled
SCTP connections to a remote host.
\value SctpListeningCapability Ability to create a listening socket
and wait for an incoming SCTP connection from a remote host.
*/
#include "qnetworkproxy.h"
@ -369,7 +375,9 @@ static QNetworkProxy::Capabilities defaultCapabilitiesForType(QNetworkProxy::Pro
/* [QNetworkProxy::DefaultProxy] = */
(int(QNetworkProxy::ListeningCapability) |
int(QNetworkProxy::TunnelingCapability) |
int(QNetworkProxy::UdpTunnelingCapability)),
int(QNetworkProxy::UdpTunnelingCapability) |
int(QNetworkProxy::SctpTunnelingCapability) |
int(QNetworkProxy::SctpListeningCapability)),
/* [QNetworkProxy::Socks5Proxy] = */
(int(QNetworkProxy::TunnelingCapability) |
int(QNetworkProxy::ListeningCapability) |
@ -379,7 +387,9 @@ static QNetworkProxy::Capabilities defaultCapabilitiesForType(QNetworkProxy::Pro
/* [QNetworkProxy::NoProxy] = */
(int(QNetworkProxy::ListeningCapability) |
int(QNetworkProxy::TunnelingCapability) |
int(QNetworkProxy::UdpTunnelingCapability)),
int(QNetworkProxy::UdpTunnelingCapability) |
int(QNetworkProxy::SctpTunnelingCapability) |
int(QNetworkProxy::SctpListeningCapability)),
/* [QNetworkProxy::HttpProxy] = */
(int(QNetworkProxy::TunnelingCapability) |
int(QNetworkProxy::CachingCapability) |
@ -965,6 +975,14 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
can all be used or be left unused, depending on the
characteristics of the socket. The URL component is not used.
\row
\li SctpSocket
\li Message-oriented sockets requesting a connection to a remote
server. The peer hostname and peer port match the values passed
to QSctpSocket::connectToHost(). The local port is usually -1,
indicating the socket has no preference in which port should be
used. The URL component is not used.
\row
\li TcpServer
\li Passive server sockets that listen on a port and await
@ -981,6 +999,14 @@ template<> void QSharedDataPointer<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;
}

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -64,6 +64,7 @@ public:
enum SocketType {
TcpSocket,
UdpSocket,
SctpSocket,
UnknownSocketType = -1
};
Q_ENUM(SocketType)

View File

@ -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);

View File

@ -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;

View File

@ -289,6 +289,19 @@ bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &)
}
#endif // QT_NO_NETWORKINTERFACE
bool QHttpSocketEngine::hasPendingDatagrams() const
{
qWarning("Operation is not supported");
return false;
}
qint64 QHttpSocketEngine::pendingDatagramSize() const
{
qWarning("Operation is not supported");
return -1;
}
#endif // QT_NO_UDPSOCKET
qint64 QHttpSocketEngine::readDatagram(char *, qint64, QIpPacketHeader *, PacketHeaderOptions)
{
qWarning("Operation is not supported");
@ -305,19 +318,6 @@ qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QIpPacketHea
return -1;
}
bool QHttpSocketEngine::hasPendingDatagrams() const
{
qWarning("Operation is not supported");
return false;
}
qint64 QHttpSocketEngine::pendingDatagramSize() const
{
qWarning("Operation is not supported");
return -1;
}
#endif // QT_NO_UDPSOCKET
qint64 QHttpSocketEngine::bytesToWrite() const
{
Q_D(const QHttpSocketEngine);

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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;
}
@ -829,6 +880,9 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int))
#if !defined(IP_PKTINFO) && defined(IP_RECVIF) && defined(Q_OS_BSD4)
+ CMSG_SPACE(sizeof(sockaddr_dl))
#endif
#ifndef QT_NO_SCTP
+ CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))
#endif
+ sizeof(quintptr) - 1) / sizeof(quintptr)];
@ -848,7 +902,8 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
msg.msg_name = &aa;
msg.msg_namelen = sizeof(aa);
}
if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination)) {
if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination
| QAbstractSocketEngine::WantStreamNumber)) {
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);
}
@ -859,13 +914,27 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
} while (recvResult == -1 && errno == EINTR);
if (recvResult == -1) {
setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
switch (errno) {
#if EWOULDBLOCK-0 && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
case EAGAIN:
// No datagram was available for reading
recvResult = -2;
break;
case ECONNREFUSED:
setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString);
break;
default:
setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
}
if (header)
header->clear();
} else if (options != QAbstractSocketEngine::WantNone) {
Q_ASSERT(header);
qt_socket_getPortAndAddress(&aa, &header->senderPort, &header->senderAddress);
header->destinationPort = localPort;
header->endOfRecord = (msg.msg_flags & MSG_EOR) != 0;
// parse the ancillary data
struct cmsghdr *cmsgptr;
@ -912,6 +981,15 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
|| (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_TTL))) {
header->hopLimit = *reinterpret_cast<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",

View File

@ -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:

View File

@ -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;
}

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
{

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -0,0 +1,6 @@
CONFIG += testcase
TARGET = tst_qsctpsocket
QT = core network testlib
SOURCES += tst_qsctpsocket.cpp

View File

@ -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"

View File

@ -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 \

View File

@ -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;