qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,35 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(NOT TARGET Qt6::Network)
return()
endif()
if(NOT INTEGRITY)
qt_internal_add_example(dnslookup)
endif()
if(TARGET Qt6::Widgets)
qt_internal_add_example(blockingfortuneclient)
qt_internal_add_example(broadcastreceiver)
qt_internal_add_example(broadcastsender)
qt_internal_add_example(http)
qt_internal_add_example(threadedfortuneserver)
qt_internal_add_example(torrent)
qt_internal_add_example(multicastreceiver)
qt_internal_add_example(multicastsender)
qt_internal_add_example(fortuneclient)
qt_internal_add_example(fortuneserver)
endif()
if(QT_FEATURE_processenvironment AND TARGET Qt6::Widgets)
qt_internal_add_example(network-chat)
endif()
if(QT_FEATURE_ssl AND TARGET Qt6::Widgets)
qt_internal_add_example(securesocketclient)
endif()
if(QT_FEATURE_dtls AND TARGET Qt6::Widgets)
qt_internal_add_example(secureudpserver)
qt_internal_add_example(secureudpclient)
endif()
if(QT_FEATURE_sctp AND TARGET Qt6::Widgets)
qt_internal_add_example(multistreamserver)
qt_internal_add_example(multistreamclient)
endif()

9
examples/network/README Normal file
View File

@ -0,0 +1,9 @@
Qt is provided with an extensive set of network classes to support both
client-based and server side network programming.
These examples demonstrate the fundamental aspects of network programming
with Qt.
Documentation for these examples can be found via the Examples
link in the main Qt documentation.

View File

@ -0,0 +1,39 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(blockingfortuneclient LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/blockingfortuneclient")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(blockingfortuneclient
blockingclient.cpp blockingclient.h
fortunethread.cpp fortunethread.h
main.cpp
)
set_target_properties(blockingfortuneclient PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(blockingfortuneclient PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS blockingfortuneclient
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,133 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtNetwork>
#include "blockingclient.h"
BlockingClient::BlockingClient(QWidget *parent)
: QWidget(parent)
{
hostLabel = new QLabel(tr("&Server name:"));
portLabel = new QLabel(tr("S&erver port:"));
// find out which IP to connect to
QString ipAddress;
const QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// use the first non-localhost IPv4 address
for (const QHostAddress &entry : ipAddressesList) {
if (entry != QHostAddress::LocalHost && entry.toIPv4Address()) {
ipAddress = entry.toString();
break;
}
}
// if we did not find one, use IPv4 localhost
if (ipAddress.isEmpty())
ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
hostLineEdit = new QLineEdit(ipAddress);
portLineEdit = new QLineEdit;
portLineEdit->setValidator(new QIntValidator(1, 65535, this));
hostLabel->setBuddy(hostLineEdit);
portLabel->setBuddy(portLineEdit);
statusLabel = new QLabel(tr("This examples requires that you run the "
"Fortune Server example as well."));
statusLabel->setWordWrap(true);
getFortuneButton = new QPushButton(tr("Get Fortune"));
getFortuneButton->setDefault(true);
getFortuneButton->setEnabled(false);
quitButton = new QPushButton(tr("Quit"));
buttonBox = new QDialogButtonBox;
buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
connect(getFortuneButton, &QPushButton::clicked,
this, &BlockingClient::requestNewFortune);
connect(quitButton, &QPushButton::clicked,
this, &BlockingClient::close);
connect(hostLineEdit, &QLineEdit::textChanged,
this, &BlockingClient::enableGetFortuneButton);
connect(portLineEdit, &QLineEdit::textChanged,
this, &BlockingClient::enableGetFortuneButton);
//! [0]
connect(&thread, &FortuneThread::newFortune,
this, &BlockingClient::showFortune);
connect(&thread, &FortuneThread::error,
this, &BlockingClient::displayError);
//! [0]
QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(hostLabel, 0, 0);
mainLayout->addWidget(hostLineEdit, 0, 1);
mainLayout->addWidget(portLabel, 1, 0);
mainLayout->addWidget(portLineEdit, 1, 1);
mainLayout->addWidget(statusLabel, 2, 0, 1, 2);
mainLayout->addWidget(buttonBox, 3, 0, 1, 2);
setLayout(mainLayout);
setWindowTitle(tr("Blocking Fortune Client"));
portLineEdit->setFocus();
}
//! [1]
void BlockingClient::requestNewFortune()
{
getFortuneButton->setEnabled(false);
thread.requestNewFortune(hostLineEdit->text(),
portLineEdit->text().toInt());
}
//! [1]
//! [2]
void BlockingClient::showFortune(const QString &nextFortune)
{
if (nextFortune == currentFortune) {
requestNewFortune();
return;
}
//! [2]
//! [3]
currentFortune = nextFortune;
statusLabel->setText(currentFortune);
getFortuneButton->setEnabled(true);
}
//! [3]
void BlockingClient::displayError(int socketError, const QString &message)
{
switch (socketError) {
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(this, tr("Blocking Fortune Client"),
tr("The host was not found. Please check the "
"host and port settings."));
break;
case QAbstractSocket::ConnectionRefusedError:
QMessageBox::information(this, tr("Blocking Fortune Client"),
tr("The connection was refused by the peer. "
"Make sure the fortune server is running, "
"and check that the host name and port "
"settings are correct."));
break;
default:
QMessageBox::information(this, tr("Blocking Fortune Client"),
tr("The following error occurred: %1.")
.arg(message));
}
getFortuneButton->setEnabled(true);
}
void BlockingClient::enableGetFortuneButton()
{
bool enable(!hostLineEdit->text().isEmpty() && !portLineEdit->text().isEmpty());
getFortuneButton->setEnabled(enable);
}

View File

@ -0,0 +1,48 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef BLOCKINGCLIENT_H
#define BLOCKINGCLIENT_H
#include <QWidget>
#include "fortunethread.h"
QT_BEGIN_NAMESPACE
class QDialogButtonBox;
class QLabel;
class QLineEdit;
class QPushButton;
class QAction;
QT_END_NAMESPACE
//! [0]
class BlockingClient : public QWidget
{
Q_OBJECT
public:
BlockingClient(QWidget *parent = nullptr);
private slots:
void requestNewFortune();
void showFortune(const QString &fortune);
void displayError(int socketError, const QString &message);
void enableGetFortuneButton();
private:
QLabel *hostLabel;
QLabel *portLabel;
QLineEdit *hostLineEdit;
QLineEdit *portLineEdit;
QLabel *statusLabel;
QPushButton *getFortuneButton;
QPushButton *quitButton;
QDialogButtonBox *buttonBox;
FortuneThread thread;
QString currentFortune;
};
//! [0]
#endif

View File

@ -0,0 +1,12 @@
QT += network widgets
HEADERS = blockingclient.h \
fortunethread.h
SOURCES = blockingclient.cpp \
main.cpp \
fortunethread.cpp
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/blockingfortuneclient
INSTALLS += target

View File

@ -0,0 +1,90 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtNetwork>
#include "fortunethread.h"
FortuneThread::FortuneThread(QObject *parent)
: QThread(parent), quit(false)
{
}
//! [0]
FortuneThread::~FortuneThread()
{
mutex.lock();
quit = true;
cond.wakeOne();
mutex.unlock();
wait();
}
//! [0]
//! [1] //! [2]
void FortuneThread::requestNewFortune(const QString &hostName, quint16 port)
{
//! [1]
QMutexLocker locker(&mutex);
this->hostName = hostName;
this->port = port;
//! [3]
if (!isRunning())
start();
else
cond.wakeOne();
}
//! [2] //! [3]
//! [4]
void FortuneThread::run()
{
mutex.lock();
//! [4] //! [5]
QString serverName = hostName;
quint16 serverPort = port;
mutex.unlock();
//! [5]
//! [6]
while (!quit) {
//! [7]
const int Timeout = 5 * 1000;
QTcpSocket socket;
socket.connectToHost(serverName, serverPort);
//! [6] //! [8]
if (!socket.waitForConnected(Timeout)) {
emit error(socket.error(), socket.errorString());
return;
}
//! [8] //! [11]
QDataStream in(&socket);
in.setVersion(QDataStream::Qt_6_5);
QString fortune;
//! [11] //! [12]
do {
if (!socket.waitForReadyRead(Timeout)) {
emit error(socket.error(), socket.errorString());
return;
}
in.startTransaction();
in >> fortune;
} while (!in.commitTransaction());
//! [12] //! [15]
mutex.lock();
emit newFortune(fortune);
//! [7]
cond.wait(&mutex);
serverName = hostName;
serverPort = port;
mutex.unlock();
}
//! [15]
}

View File

@ -0,0 +1,36 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef FORTUNETHREAD_H
#define FORTUNETHREAD_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
//! [0]
class FortuneThread : public QThread
{
Q_OBJECT
public:
FortuneThread(QObject *parent = nullptr);
~FortuneThread();
void requestNewFortune(const QString &hostName, quint16 port);
void run() override;
signals:
void newFortune(const QString &fortune);
void error(int socketError, const QString &message);
private:
QString hostName;
quint16 port;
QMutex mutex;
QWaitCondition cond;
bool quit;
};
//! [0]
#endif

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "blockingclient.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
BlockingClient client;
client.show();
return app.exec();
}

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(broadcastreceiver LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/broadcastreceiver")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(broadcastreceiver
main.cpp
receiver.cpp receiver.h
)
set_target_properties(broadcastreceiver PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(broadcastreceiver PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS broadcastreceiver
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,11 @@
QT += network widgets
requires(qtConfig(udpsocket))
HEADERS = receiver.h
SOURCES = receiver.cpp \
main.cpp
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/broadcastreceiver
INSTALLS += target

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "receiver.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Receiver receiver;
receiver.show();
return app.exec();
}

View File

@ -0,0 +1,56 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QLabel>
#include <QPushButton>
#include <QUdpSocket>
#include <QVBoxLayout>
#include <QCoreApplication>
#include "receiver.h"
Receiver::Receiver(QWidget *parent)
: QWidget(parent)
{
statusLabel = new QLabel(tr("Listening for broadcasted messages"));
statusLabel->setWordWrap(true);
auto quitButton = new QPushButton(tr("&Quit"));
//! [0]
udpSocket = new QUdpSocket(this);
udpSocket->bind(45454, QUdpSocket::ShareAddress);
//! [0]
//! [1]
connect(udpSocket, &QUdpSocket::readyRead,
this, &Receiver::processPendingDatagrams);
//! [1]
connect(quitButton, &QPushButton::clicked,
qApp, &QCoreApplication::quit);
auto buttonLayout = new QHBoxLayout;
buttonLayout->addStretch(1);
buttonLayout->addWidget(quitButton);
buttonLayout->addStretch(1);
auto mainLayout = new QVBoxLayout;
mainLayout->addWidget(statusLabel);
mainLayout->addLayout(buttonLayout);
setLayout(mainLayout);
setWindowTitle(tr("Broadcast Receiver"));
}
void Receiver::processPendingDatagrams()
{
QByteArray datagram;
//! [2]
while (udpSocket->hasPendingDatagrams()) {
datagram.resize(int(udpSocket->pendingDatagramSize()));
udpSocket->readDatagram(datagram.data(), datagram.size());
statusLabel->setText(tr("Received datagram: \"%1\"")
.arg(datagram.constData()));
}
//! [2]
}

View File

@ -0,0 +1,29 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QWidget>
QT_BEGIN_NAMESPACE
class QLabel;
class QUdpSocket;
QT_END_NAMESPACE
class Receiver : public QWidget
{
Q_OBJECT
public:
explicit Receiver(QWidget *parent = nullptr);
private slots:
void processPendingDatagrams();
private:
QLabel *statusLabel = nullptr;
QUdpSocket *udpSocket = nullptr;
};
#endif

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(broadcastsender LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/broadcastsender")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(broadcastsender
main.cpp
sender.cpp sender.h
)
set_target_properties(broadcastsender PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(broadcastsender PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS broadcastsender
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,10 @@
QT += network widgets
requires(qtConfig(udpsocket))
HEADERS = sender.h
SOURCES = sender.cpp \
main.cpp
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/broadcastsender
INSTALLS += target

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "sender.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Sender sender;
sender.show();
return app.exec();
}

View File

@ -0,0 +1,53 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtNetwork>
#include <QtCore>
#include "sender.h"
Sender::Sender(QWidget *parent)
: QWidget(parent)
{
statusLabel = new QLabel(tr("Ready to broadcast datagrams on port 45454"));
statusLabel->setWordWrap(true);
startButton = new QPushButton(tr("&Start"));
auto quitButton = new QPushButton(tr("&Quit"));
auto buttonBox = new QDialogButtonBox;
buttonBox->addButton(startButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
//! [0]
udpSocket = new QUdpSocket(this);
//! [0]
connect(startButton, &QPushButton::clicked, this, &Sender::startBroadcasting);
connect(quitButton, &QPushButton::clicked, qApp, &QCoreApplication::quit);
connect(&timer, &QTimer::timeout, this, &Sender::broadcastDatagram);
auto mainLayout = new QVBoxLayout;
mainLayout->addWidget(statusLabel);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setWindowTitle(tr("Broadcast Sender"));
}
void Sender::startBroadcasting()
{
startButton->setEnabled(false);
timer.start(1000);
}
void Sender::broadcastDatagram()
{
statusLabel->setText(tr("Now broadcasting datagram %1").arg(messageNo));
//! [1]
QByteArray datagram = "Broadcast message " + QByteArray::number(messageNo);
udpSocket->writeDatagram(datagram, QHostAddress::Broadcast, 45454);
//! [1]
++messageNo;
}

View File

@ -0,0 +1,35 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SENDER_H
#define SENDER_H
#include <QWidget>
#include <QTimer>
QT_BEGIN_NAMESPACE
class QLabel;
class QPushButton;
class QUdpSocket;
QT_END_NAMESPACE
class Sender : public QWidget
{
Q_OBJECT
public:
explicit Sender(QWidget *parent = nullptr);
private slots:
void startBroadcasting();
void broadcastDatagram();
private:
QLabel *statusLabel = nullptr;
QPushButton *startButton = nullptr;
QUdpSocket *udpSocket = nullptr;
QTimer timer;
int messageNo = 1;
};
#endif

View File

@ -0,0 +1,30 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(dnslookup LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/dnslookup")
find_package(Qt6 REQUIRED COMPONENTS Core Network)
qt_standard_project_setup()
qt_add_executable(dnslookup
dnslookup.cpp dnslookup.h
)
target_link_libraries(dnslookup PRIVATE
Qt6::Core
Qt6::Network
)
install(TARGETS dnslookup
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,224 @@
// Copyright (C) 2016 Jeremy Lainé <jeremy.laine@m4x.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "dnslookup.h"
#include <QCoreApplication>
#include <QDnsLookup>
#include <QHostAddress>
#include <QStringList>
#include <QTimer>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <cstdio>
using namespace Qt::StringLiterals;
static std::optional<QDnsLookup::Type> typeFromParameter(QStringView type)
{
if (type.compare(u"a", Qt::CaseInsensitive) == 0)
return QDnsLookup::A;
if (type.compare(u"aaaa", Qt::CaseInsensitive) == 0)
return QDnsLookup::AAAA;
if (type.compare(u"any", Qt::CaseInsensitive) == 0)
return QDnsLookup::ANY;
if (type.compare(u"cname", Qt::CaseInsensitive) == 0)
return QDnsLookup::CNAME;
if (type.compare(u"mx", Qt::CaseInsensitive) == 0)
return QDnsLookup::MX;
if (type.compare(u"ns", Qt::CaseInsensitive) == 0)
return QDnsLookup::NS;
if (type.compare(u"ptr", Qt::CaseInsensitive) == 0)
return QDnsLookup::PTR;
if (type.compare(u"srv", Qt::CaseInsensitive) == 0)
return QDnsLookup::SRV;
if (type.compare(u"txt", Qt::CaseInsensitive) == 0)
return QDnsLookup::TXT;
return std::nullopt;
}
//! [0]
struct CommandLineParseResult
{
enum class Status {
Ok,
Error,
VersionRequested,
HelpRequested
};
Status statusCode = Status::Ok;
std::optional<QString> errorString = std::nullopt;
};
CommandLineParseResult parseCommandLine(QCommandLineParser &parser, DnsQuery *query)
{
using Status = CommandLineParseResult::Status;
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
const QCommandLineOption nameServerOption("n", "The name server to use.", "nameserver");
parser.addOption(nameServerOption);
const QCommandLineOption typeOption("t", "The lookup type.", "type");
parser.addOption(typeOption);
parser.addPositionalArgument("name", "The name to look up.");
const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption versionOption = parser.addVersionOption();
if (!parser.parse(QCoreApplication::arguments()))
return { Status::Error, parser.errorText() };
if (parser.isSet(versionOption))
return { Status::VersionRequested };
if (parser.isSet(helpOption))
return { Status::HelpRequested };
if (parser.isSet(nameServerOption)) {
const QString nameserver = parser.value(nameServerOption);
query->nameServer = QHostAddress(nameserver);
if (query->nameServer.isNull()
|| query->nameServer.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) {
return { Status::Error,
u"Bad nameserver address: %1"_qs.arg(nameserver) };
}
}
if (parser.isSet(typeOption)) {
const QString typeParameter = parser.value(typeOption);
if (std::optional<QDnsLookup::Type> type = typeFromParameter(typeParameter))
query->type = *type;
else
return { Status::Error, u"Bad record type: %1"_qs.arg(typeParameter) };
}
const QStringList positionalArguments = parser.positionalArguments();
if (positionalArguments.isEmpty())
return { Status::Error, u"Argument 'name' missing."_qs };
if (positionalArguments.size() > 1)
return { Status::Error, u"Several 'name' arguments specified."_qs };
query->name = positionalArguments.first();
return { Status::Ok };
}
//! [0]
DnsManager::DnsManager()
: dns(new QDnsLookup(this))
{
connect(dns, &QDnsLookup::finished, this, &DnsManager::showResults);
}
void DnsManager::execute()
{
// lookup type
dns->setType(query.type);
if (!query.nameServer.isNull())
dns->setNameserver(query.nameServer);
dns->setName(query.name);
dns->lookup();
}
void DnsManager::showResults()
{
if (dns->error() != QDnsLookup::NoError)
std::printf("Error: %i (%s)\n", dns->error(), qPrintable(dns->errorString()));
// CNAME records
const QList<QDnsDomainNameRecord> cnameRecords = dns->canonicalNameRecords();
for (const QDnsDomainNameRecord &record : cnameRecords) {
std::printf("%s\t%i\tIN\tCNAME\t%s\n", qPrintable(record.name()), record.timeToLive(),
qPrintable(record.value()));
}
// A and AAAA records
const QList<QDnsHostAddressRecord> aRecords = dns->hostAddressRecords();
for (const QDnsHostAddressRecord &record : aRecords) {
const char *type =
(record.value().protocol() == QAbstractSocket::IPv6Protocol) ? "AAAA" : "A";
std::printf("%s\t%i\tIN\t%s\t%s\n", qPrintable(record.name()), record.timeToLive(), type,
qPrintable(record.value().toString()));
}
// MX records
const QList<QDnsMailExchangeRecord> mxRecords = dns->mailExchangeRecords();
for (const QDnsMailExchangeRecord &record : mxRecords) {
std::printf("%s\t%i\tIN\tMX\t%u %s\n", qPrintable(record.name()), record.timeToLive(),
record.preference(), qPrintable(record.exchange()));
}
// NS records
const QList<QDnsDomainNameRecord> nsRecords = dns->nameServerRecords();
for (const QDnsDomainNameRecord &record : nsRecords) {
std::printf("%s\t%i\tIN\tNS\t%s\n", qPrintable(record.name()), record.timeToLive(),
qPrintable(record.value()));
}
// PTR records
const QList<QDnsDomainNameRecord> ptrRecords = dns->pointerRecords();
for (const QDnsDomainNameRecord &record : ptrRecords) {
std::printf("%s\t%i\tIN\tPTR\t%s\n", qPrintable(record.name()), record.timeToLive(),
qPrintable(record.value()));
}
// SRV records
const QList<QDnsServiceRecord> srvRecords = dns->serviceRecords();
for (const QDnsServiceRecord &record : srvRecords) {
std::printf("%s\t%i\tIN\tSRV\t%u %u %u %s\n", qPrintable(record.name()),
record.timeToLive(), record.priority(), record.weight(), record.port(),
qPrintable(record.target()));
}
// TXT records
const QList<QDnsTextRecord> txtRecords = dns->textRecords();
for (const QDnsTextRecord &record : txtRecords) {
QStringList values;
const QList<QByteArray> dnsRecords = record.values();
for (const QByteArray &ba : dnsRecords)
values << "\"" + QString::fromLatin1(ba) + "\"";
std::printf("%s\t%i\tIN\tTXT\t%s\n", qPrintable(record.name()), record.timeToLive(),
qPrintable(values.join(' ')));
}
QCoreApplication::instance()->quit();
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
//! [1]
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
QCoreApplication::setApplicationName(QCoreApplication::translate("QDnsLookupExample",
"DNS Lookup Example"));
QCommandLineParser parser;
parser.setApplicationDescription(QCoreApplication::translate("QDnsLookupExample",
"An example demonstrating the "
"class QDnsLookup."));
DnsQuery query;
using Status = CommandLineParseResult::Status;
CommandLineParseResult parseResult = parseCommandLine(parser, &query);
switch (parseResult.statusCode) {
case Status::Ok:
break;
case Status::Error:
std::fputs(qPrintable(parseResult.errorString.value_or(u"Unknown error occurred"_qs)),
stderr);
std::fputs("\n\n", stderr);
std::fputs(qPrintable(parser.helpText()), stderr);
return 1;
case Status::VersionRequested:
parser.showVersion();
Q_UNREACHABLE_RETURN(0);
case Status::HelpRequested:
parser.showHelp();
Q_UNREACHABLE_RETURN(0);
}
//! [1]
DnsManager manager;
manager.setQuery(query);
QTimer::singleShot(0, &manager, SLOT(execute()));
return app.exec();
}

View File

@ -0,0 +1,36 @@
// Copyright (C) 2016 Jeremy Lainé <jeremy.laine@m4x.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QDnsLookup>
#include <QHostAddress>
//! [0]
struct DnsQuery
{
DnsQuery() : type(QDnsLookup::A) {}
QDnsLookup::Type type;
QHostAddress nameServer;
QString name;
};
//! [0]
class DnsManager : public QObject
{
Q_OBJECT
public:
DnsManager();
void setQuery(const DnsQuery &q) { query = q; }
public slots:
void execute();
void showResults();
private:
QDnsLookup *dns;
DnsQuery query;
};

View File

@ -0,0 +1,9 @@
TEMPLATE = app
QT = core network
CONFIG += cmdline
HEADERS += dnslookup.h
SOURCES += dnslookup.cpp
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/dnslookup
INSTALLS += target

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,175 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example blockingfortuneclient
\title Blocking Fortune Client
\examplecategory {Networking}
\meta tags {tcp,network,threading,synchronous-io}
\ingroup examples-network
\brief Demonstrates how to create a client for a network service.
\image blockingfortuneclient-example.png
QTcpSocket supports two general approaches to network programming:
\list
\li \e{The asynchronous (non-blocking) approach.} Operations are scheduled
and performed when control returns to Qt's event loop. When the operation
is finished, QTcpSocket emits a signal. For example,
QTcpSocket::connectToHost() returns immediately, and when the connection
has been established, QTcpSocket emits
\l{QTcpSocket::connected()}{connected()}.
\li \e{The synchronous (blocking) approach.} In non-GUI and multithreaded
applications, you can call the \c waitFor...() functions (e.g.,
QTcpSocket::waitForConnected()) to suspend the calling thread until the
operation has completed, instead of connecting to signals.
\endlist
The implementation is very similar to the
\l{fortuneclient}{Fortune Client} example, but instead of having
QTcpSocket as a member of the main class, doing asynchronous networking in
the main thread, we will do all network operations in a separate thread
and use QTcpSocket's blocking API.
The purpose of this example is to demonstrate a pattern that you can use
to simplify your networking code, without losing responsiveness in your
user interface. Use of Qt's blocking network API often leads to
simpler code, but because of its blocking behavior, it should only be used
in non-GUI threads to prevent the user interface from freezing. But
contrary to what many think, using threads with QThread does not
necessarily add unmanagable complexity to your application.
We will start with the FortuneThread class, which handles the network
code.
\snippet blockingfortuneclient/fortunethread.h 0
FortuneThread is a QThread subclass that provides an API for scheduling
requests for fortunes, and it has signals for delivering fortunes and
reporting errors. You can call requestNewFortune() to request a new
fortune, and the result is delivered by the newFortune() signal. If any
error occurs, the error() signal is emitted.
It's important to notice that requestNewFortune() is called from the main,
GUI thread, but the host name and port values it stores will be accessed
from FortuneThread's thread. Because we will be reading and writing
FortuneThread's data members from different threads concurrently, we use
QMutex to synchronize access.
\snippet blockingfortuneclient/fortunethread.cpp 2
The requestNewFortune() function stores the host name and port of the
fortune server as member data, and we lock the mutex with QMutexLocker to
protect this data. We then start the thread, unless it is already
running. We will come back to the QWaitCondition::wakeOne() call later.
\snippet blockingfortuneclient/fortunethread.cpp 4
\snippet blockingfortuneclient/fortunethread.cpp 5
In the run() function, we start by acquiring the mutex lock, fetching the
host name and port from the member data, and then releasing the lock
again. The case that we are protecting ourselves against is that \c
requestNewFortune() could be called at the same time as we are fetching
this data. QString is \l reentrant but \e not \l{thread-safe}, and we must
also avoid the unlikely risk of reading the host name from one request,
and port of another. And as you might have guessed, FortuneThread can only
handle one request at a time.
The run() function now enters a loop:
\snippet blockingfortuneclient/fortunethread.cpp 6
The loop will continue requesting fortunes for as long as \e quit is
false. We start our first request by creating a QTcpSocket on the stack,
and then we call \l{QTcpSocket::connectToHost()}{connectToHost()}. This
starts an asynchronous operation which, after control returns to Qt's
event loop, will cause QTcpSocket to emit
\l{QTcpSocket::connected()}{connected()} or
\l{QTcpSocket::error()}{error()}.
\snippet blockingfortuneclient/fortunethread.cpp 8
But since we are running in a non-GUI thread, we do not have to worry
about blocking the user interface. So instead of entering an event loop,
we simply call QTcpSocket::waitForConnected(). This function will wait,
blocking the calling thread, until QTcpSocket emits connected() or an
error occurs. If connected() is emitted, the function returns true; if the
connection failed or timed out (which in this example happens after 5
seconds), false is returned. QTcpSocket::waitForConnected(), like the
other \c waitFor...() functions, is part of QTcpSocket's \e{blocking
API}.
After this statement, we have a connected socket to work with.
\snippet blockingfortuneclient/fortunethread.cpp 11
Now we can create a QDataStream object, passing the socket to
QDataStream's constructor, and as in the other client examples we set
the stream protocol version to QDataStream::Qt_4_0.
\snippet blockingfortuneclient/fortunethread.cpp 12
We proceed by initiating a loop that waits for the fortune string data by
calling QTcpSocket::waitForReadyRead(). If it returns false, we abort the
operation. After this statement, we start a stream read transaction. We
exit the loop when QDataStream::commitTransaction() returns true, which
means successful fortune string loading. The resulting fortune is
delivered by emitting newFortune():
\snippet blockingfortuneclient/fortunethread.cpp 15
The final part of our loop is that we acquire the mutex so that we can
safely read from our member data. We then let the thread go to sleep by
calling QWaitCondition::wait(). At this point, we can go back to
requestNewFortune() and look closely at the call to wakeOne():
\snippet blockingfortuneclient/fortunethread.cpp 1
\dots
\snippet blockingfortuneclient/fortunethread.cpp 3
What happened here was that because the thread falls asleep waiting for a
new request, we needed to wake it up again when a new request
arrives. QWaitCondition is often used in threads to signal a wakeup call
like this.
\snippet blockingfortuneclient/fortunethread.cpp 0
Finishing off the FortuneThread walkthrough, this is the destructor that
sets \e quit to true, wakes up the thread and waits for the thread to exit
before returning. This lets the \c while loop in run() will finish its current
iteration. When run() returns, the thread will terminate and be destroyed.
Now for the BlockingClient class:
\snippet blockingfortuneclient/blockingclient.h 0
BlockingClient is very similar to the Client class in the
\l{fortuneclient}{Fortune Client} example, but in this class
we store a FortuneThread member instead of a pointer to a QTcpSocket.
When the user clicks the "Get Fortune" button, the same slot is called,
but its implementation is slightly different:
\snippet blockingfortuneclient/blockingclient.cpp 0
We connect our FortuneThread's two signals newFortune() and error() (which
are somewhat similar to QTcpSocket::readyRead() and QTcpSocket::error() in
the previous example) to requestNewFortune() and displayError().
\snippet blockingfortuneclient/blockingclient.cpp 1
The requestNewFortune() slot calls FortuneThread::requestNewFortune(),
which \e schedules the request. When the thread has received a new fortune
and emits newFortune(), our showFortune() slot is called:
\snippet blockingfortuneclient/blockingclient.cpp 2
\codeline
\snippet blockingfortuneclient/blockingclient.cpp 3
Here, we simply display the fortune we received as the argument.
\sa {Fortune Client}, {Fortune Server}
*/

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example broadcastreceiver
\title Broadcast Receiver Example
\ingroup examples-network
\brief Demonstrates how to receive information broadcasted over a local network.
This example uses the Qt Network APIs to demonstrate how to receive
messages broadcasted over a local network.
\image broadcastreceiver-example.png
*/

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example broadcastsender
\title Broadcast Sender Example
\ingroup examples-network
\brief Demonstrates how to broadcast information to multiple clients on a local network.
This example uses Qt Network APIs to demonstrate how to broadcast messages
to multiple clients over a local network.
\image broadcastsender-example.png
*/

View File

@ -0,0 +1,138 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example fortuneclient
\title Fortune Client
\examplecategory {Networking}
\meta tags {tcp,network}
\ingroup examples-network
\brief Demonstrates how to create a client for a network service.
This example uses QTcpSocket, and is intended to be run alongside the
\l{fortuneserver}{Fortune Server} example or
the \l{threadedfortuneserver}{Threaded Fortune Server} example.
\image fortuneclient-example.png Screenshot of the Fortune Client example
This example uses a simple QDataStream-based data transfer protocol to
request a line of text from a fortune server (from the
\l{fortuneserver}{Fortune Server} example). The client requests a
fortune by simply connecting to the server. The server then responds with
a QString which contains the fortune text.
QTcpSocket supports two general approaches to network programming:
\list
\li \e{The asynchronous (non-blocking) approach.} Operations are scheduled
and performed when control returns to Qt's event loop. When the operation
is finished, QTcpSocket emits a signal. For example,
QTcpSocket::connectToHost() returns immediately, and when the connection
has been established, QTcpSocket emits
\l{QTcpSocket::connected()}{connected()}.
\li \e{The synchronous (blocking) approach.} In non-GUI and multithreaded
applications, you can call the \c waitFor...() functions (e.g.,
QTcpSocket::waitForConnected()) to suspend the calling thread until the
operation has completed, instead of connecting to signals.
\endlist
In this example, we will demonstrate the asynchronous approach. The
\l{blockingfortuneclient}{Blocking Fortune Client} example
illustrates the synchronous approach.
Our class contains some data and a few private slots:
\snippet fortuneclient/client.h 0
Other than the widgets that make up the GUI, the data members include a
QTcpSocket pointer, a QDataStream object that operates on the socket, and
a copy of the fortune text currently displayed.
The socket is initialized in the Client constructor. We'll pass the main
widget as parent, so that we won't have to worry about deleting the
socket:
\snippet fortuneclient/client.cpp 0
\dots
\snippet fortuneclient/client.cpp 1
The protocol is based on QDataStream, so we set the stream device to the
newly created socket. We then explicitly set the protocol version of the
stream to QDataStream::Qt_4_0 to ensure that we're using the same version
as the fortune server, no matter which version of Qt the client and
server use.
The only QTcpSocket signals we need in this example are
QTcpSocket::readyRead(), signifying that data has been received, and
QTcpSocket::errorOccurred(), which we will use to catch any connection errors:
\dots
\snippet fortuneclient/client.cpp 3
\dots
\snippet fortuneclient/client.cpp 5
Clicking the \uicontrol{Get Fortune} button will invoke the \c
requestNewFortune() slot:
\snippet fortuneclient/client.cpp 6
Because we allow the user to click \uicontrol{Get Fortune} before the
previous connection finished closing, we start off by aborting the
previous connection by calling QTcpSocket::abort(). (On an unconnected
socket, this function does nothing.) We then proceed to connecting to the
fortune server by calling QTcpSocket::connectToHost(), passing the
hostname and port from the user interface as arguments.
As a result of calling \l{QTcpSocket::connectToHost()}{connectToHost()},
one of two things can happen:
\list
\li \e{The connection is established.} In this case, the server will send us a
fortune. QTcpSocket will emit \l{QTcpSocket::readyRead()}{readyRead()}
every time it receives a block of data.
\li \e{An error occurs.} We need to inform the user if the connection
failed or was broken. In this case, QTcpSocket will emit
\l{QTcpSocket::errorOccurred()}{errorOccurred()}, and \c Client::displayError() will be
called.
\endlist
Let's go through the \l{QTcpSocket::errorOccurred()}{errorOccurred()} case first:
\snippet fortuneclient/client.cpp 13
We pop up all errors in a dialog using
QMessageBox::information(). QTcpSocket::RemoteHostClosedError is silently
ignored, because the fortune server protocol ends with the server closing
the connection.
Now for the \l{QTcpSocket::readyRead()}{readyRead()} alternative. This
signal is connected to \c Client::readFortune():
\snippet fortuneclient/client.cpp 8
Now, TCP is based on sending a stream of data, so we cannot expect to get
the entire fortune in one go. Especially on a slow network, the data can
be received in several small fragments. QTcpSocket buffers up all incoming
data and emits \l{QTcpSocket::readyRead()}{readyRead()} for every new
block that arrives, and it is our job to ensure that we have received all
the data we need before we start parsing.
For this purpose we use a QDataStream read transaction. It keeps reading
stream data into an internal buffer and rolls it back in case of an
incomplete read. We start by calling startTransaction() which also resets
the stream status to indicate that new data was received on the socket.
We proceed by using QDataStream's streaming operator to read the fortune
from the socket into a QString. Once read, we complete the transaction by
calling QDataStream::commitTransaction(). If we did not receive a full
packet, this function restores the stream data to the initial position,
after which we can wait for a new readyRead() signal.
After a successful read transaction, we call QLabel::setText() to display
the fortune.
\sa {Fortune Server}, {Blocking Fortune Client}
*/

View File

@ -0,0 +1,75 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example fortuneserver
\title Fortune Server
\examplecategory {Networking}
\meta tags {tcp,network,server}
\ingroup examples-network
\brief Demonstrates how to create a server for a network service.
This example is intended to be run alongside the
\l{fortuneclient}{Fortune Client} example or the
\l{blockingfortuneclient}{Blocking Fortune Client} example.
\image fortuneserver-example.png Screenshot of the Fortune Server example
It uses QTcpServer to accept incoming TCP connections, and a
simple QDataStream based data transfer protocol to write a fortune to the
connecting client (from the \l{fortuneclient}{Fortune Client}
example), before closing the connection.
\snippet fortuneserver/server.h 0
The server is implemented using a simple class with only one slot, for
handling incoming connections.
\snippet fortuneserver/server.cpp 1
In its constructor, our Server object calls QTcpServer::listen() to set up
a QTcpServer to listen on all addresses, on an arbitrary port. In then
displays the port QTcpServer picked in a label, so that user knows which
port the fortune client should connect to.
\snippet fortuneserver/server.cpp 2
Our server generates a list of random fortunes that it can send to
connecting clients.
\snippet fortuneserver/server.cpp 3
When a client connects to our server, QTcpServer will emit
QTcpServer::newConnection(). In turn, this will invoke our
sendFortune() slot:
\snippet fortuneserver/server.cpp 4
The purpose of this slot is to select a random line from our list of
fortunes, encode it into a QByteArray using QDataStream, and then write it
to the connecting socket. This is a common way to transfer binary data
using QTcpSocket. First we create a QByteArray and a QDataStream object,
passing the bytearray to QDataStream's constructor. We then explicitly set
the protocol version of QDataStream to QDataStream::Qt_5_10 to ensure that
we can communicate with clients from future versions of Qt (see
QDataStream::setVersion()). We continue by streaming in a random fortune.
\snippet fortuneserver/server.cpp 7
We then call QTcpServer::nextPendingConnection(), which returns the
QTcpSocket representing the server side of the connection. By connecting
QTcpSocket::disconnected() to QObject::deleteLater(), we ensure that the
socket will be deleted after disconnecting.
\snippet fortuneserver/server.cpp 8
The encoded fortune is written using QTcpSocket::write(), and we finally
call QTcpSocket::disconnectFromHost(), which will close the connection
after QTcpSocket has finished writing the fortune to the network. Because
QTcpSocket works asynchronously, the data will be written after this
function returns, and control goes back to Qt's event loop. The socket
will then close, which in turn will cause QObject::deleteLater() to delete
it.
\sa {Fortune Client}, {Threaded Fortune Server}
*/

View File

@ -0,0 +1,81 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example http
\examplecategory {Networking}
\meta tags {http,network,https,proxy}
\title HTTP Client
\ingroup examples-network
\brief Demonstrates a simple HTTP client.
This example demonstrates how a simple HTTP client can fetch files
from remote hosts.
\image http-example.webp
The main work of this example is done in the HttpWindow class.
Thus we will focus on that.
\snippet http/httpwindow.cpp qnam-download
Using QNetworkAccessManager, we begin the download of a resource as
pointed to by the \c url. If you are unfamiliar with it or the function used,
QNetworkAccessManager::get(), or simply want to look into it in more detail,
take a look at its documentation and the documentation for
QNetworkReply and QNetworkRequest.
\snippet http/httpwindow.cpp connecting-reply-to-slots
Above, we connect some of the reply's signals to slots in the class.
These slots will take care of both incoming data and finalizing the
download/handling errors.
\snippet http/httpwindow.cpp networkreply-readyread-1
As for handling the incoming data, since we don't know the maximum
download size of any potential input and we don't want to exhaust
the memory of any computer which might run the example program, we
handle incoming data in QNetworkReply::readyRead() instead of in
QNetworkReply::finished().
\snippet http/httpwindow.cpp networkreply-readyread-2
Then we write the data to file as it arrives. It is less convenient,
but the application will consume less memory at its peak!
\snippet http/httpwindow.cpp sslerrors-1
With the QNetworkReply::sslErrors() signal we can also handle errors that may
occur during the TLS handshake when connecting to secure websites (i.e. HTTPS).
\snippet http/httpwindow.cpp sslerrors-2
In this example, we show a dialog to the user so that they can choose whether
or not to ignore the errors.
\snippet http/httpwindow.cpp networkreply-error-handling-1
\snippet http/httpwindow.cpp networkreply-error-handling-2
If an error occurs then QNetworkReply will emit the
QNetworkReply::errorOccurred() signal, followed by the
QNetworkReply::finished() signal. In this example, we only connect to the
latter. We handle any potential error(s) in the respective slot by deleting
the file we were writing to, and display the error with our status label.
\snippet http/httpwindow.cpp qnam-auth-required-1
If you connect to a website that uses
\l{https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication}{HTTP authentication},
assuming you didn't supply the credentials that should be used ahead of time,
you can handle missing credentials when the website requests it. With QNetworkAccessManager,
we do this in a slot connected to the signal
QNetworkAccessManager::authenticationRequired(). We make this connection once,
in the constructor.
\snippet http/httpwindow.cpp qnam-auth-required-2
In this example, we show a dialog where the user can either insert a
username and password, or cancel. Canceling causes the request to fail.
*/

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example multicastreceiver
\title Multicast Receiver
\examplecategory {Networking}
\meta tags {network,multicast,ipv6,ipv4,udp}
\ingroup examples-network
\brief Demonstrates how to receive information sent to a multicast group.
This example demonstrates how to receive messages sent to a multicast group.
\image multicastreceiver-example.webp
*/

View File

@ -0,0 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example multicastsender
\title Multicast Sender
\examplecategory {Networking}
\meta tags {network,multicast,ipv6,ipv4,udp}
\ingroup examples-network
\brief Demonstrates how to send messages to a multicast group.
This example demonstrates how to send messages to the clients of a
multicast group.
\image multicastsender-example.webp
*/

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example network-chat
\title Network Chat Example
\ingroup examples-network
\brief Demonstrates a stateful peer-to-peer Chat client.
This example uses broadcasting with QUdpSocket and QNetworkInterface to
discover its peers.
\image network-chat-example.png
*/

View File

@ -0,0 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example securesocketclient
\title Secure Socket Client Example
\ingroup examples-network
\brief Demonstrates how to communicate over an encrypted (SSL) connection.
This example uses QSslSocket to demonstrate how to communicate over an
encrypted connection, deal with authenticity problems, and display security
and certificate information.
\image securesocketclient.png
\image securesocketclient2.png
*/

View File

@ -0,0 +1,100 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example secureudpclient
\title DTLS client
\ingroup examples-network
\brief This example demonstrates how to implement client-side DTLS connections.
\image secureudpclient-example.png Screenshot of the DTLS client example.
\note The DTLS client example is intended to be run alongside the \l{secureudpserver}{DTLS server} example.
The example DTLS client can establish several DTLS connections to one
or many DTLS servers. A client-side DTLS connection is implemented by the
DtlsAssociation class. This class uses QUdpSocket to read and write datagrams
and QDtls for encryption:
\snippet secureudpclient/association.h 0
The constructor sets the minimal TLS configuration for the new DTLS connection,
and sets the address and the port of the server:
\dots
\snippet secureudpclient/association.cpp 1
\dots
The QDtls::handshakeTimeout() signal is connected to the handleTimeout() slot
to deal with packet loss and retransmission during the handshake phase:
\dots
\snippet secureudpclient/association.cpp 2
\dots
To ensure we receive only the datagrams from the server, we connect our UDP socket to the server:
\dots
\snippet secureudpclient/association.cpp 3
\dots
The QUdpSocket::readyRead() signal is connected to the readyRead() slot:
\dots
\snippet secureudpclient/association.cpp 13
\dots
When a secure connection to a server is established, a DtlsAssociation object
will be sending short ping messages to the server, using a timer:
\snippet secureudpclient/association.cpp 4
startHandshake() starts a handshake with the server:
\snippet secureudpclient/association.cpp 5
The readyRead() slot reads a datagram sent by the server:
\snippet secureudpclient/association.cpp 6
If the handshake was already completed, this datagram is decrypted:
\snippet secureudpclient/association.cpp 7
otherwise, we try to continue the handshake:
\snippet secureudpclient/association.cpp 8
When the handshake has completed, we send our first ping message:
\snippet secureudpclient/association.cpp 9
The pskRequired() slot provides the Pre-Shared Key (PSK) needed during the handshake
phase:
\snippet secureudpclient/association.cpp 14
\note For the sake of brevity, the definition of pskRequired() is oversimplified.
The documentation for the QSslPreSharedKeyAuthenticator class explains in detail
how this slot can be properly implemented.
pingTimeout() sends an encrypted message to the server:
\snippet secureudpclient/association.cpp 10
During the handshake phase the client must handle possible timeouts, which
can happen due to packet loss. The handshakeTimeout() slot retransmits
the handshake messages:
\snippet secureudpclient/association.cpp 11
Before a client connection is destroyed, its DTLS connection must be shut down:
\snippet secureudpclient/association.cpp 12
Error messages, informational messages, and decrypted responses from servers
are displayed by the UI:
\snippet secureudpclient/mainwindow.cpp 0
*/

View File

@ -0,0 +1,107 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example secureudpserver
\title DTLS server
\ingroup examples-network
\brief This examples demonstrates how to implement a simple DTLS server.
\image secureudpserver-example.png Screenshot of the DTLS server example.
\note The DTLS server example is intended to be run alongside the \l{secureudpclient}{DTLS client} example.
The server is implemented by the DtlsServer class. It uses QUdpSocket,
QDtlsClientVerifier, and QDtls to test each client's reachability, complete a handshake,
and read and write encrypted messages.
\snippet secureudpserver/server.h 0
The constructor connects the QUdpSocket::readyRead() signal to its
readyRead() slot and sets the minimal needed TLS configuration:
\snippet secureudpserver/server.cpp 1
\note The server is not using a certificate and is relying on Pre-Shared
Key (PSK) handshake.
listen() binds QUdpSocket:
\snippet secureudpserver/server.cpp 2
The readyRead() slot processes incoming datagrams:
\dots
\snippet secureudpserver/server.cpp 3
\dots
After extracting an address and a port number, the server first tests
if it's a datagram from an already known peer:
\dots
\snippet secureudpserver/server.cpp 4
\dots
If it is a new, unknown address and port, the datagram is processed as a
potential ClientHello message, sent by a DTLS client:
\dots
\snippet secureudpserver/server.cpp 5
\dots
If it's a known DTLS client, the server either decrypts the datagram:
\dots
\snippet secureudpserver/server.cpp 6
\dots
or continues a handshake with this peer:
\dots
\snippet secureudpserver/server.cpp 7
\dots
handleNewConnection() verifies it's a reachable DTLS client, or sends a
HelloVerifyRequest:
\snippet secureudpserver/server.cpp 8
\dots
If the new client was verified to be a reachable DTLS client, the server creates
and configures a new QDtls object, and starts a server-side handshake:
\dots
\snippet secureudpserver/server.cpp 9
\dots
doHandshake() progresses through the handshake phase:
\snippet secureudpserver/server.cpp 11
During the handshake phase, the QDtls::pskRequired() signal is emitted and
the pskRequired() slot provides the preshared key:
\snippet secureudpserver/server.cpp 13
\note For the sake of brevity, the definition of pskRequired() is oversimplified.
The documentation for the QSslPreSharedKeyAuthenticator class explains in detail
how this slot can be properly implemented.
After the handshake is completed for the network peer, an encrypted DTLS
connection is considered to be established and the server decrypts subsequent
datagrams, sent by the peer, by calling decryptDatagram(). The server also
sends an encrypted response to the peer:
\snippet secureudpserver/server.cpp 12
The server closes its DTLS connections by calling QDtls::shutdown():
\snippet secureudpserver/server.cpp 14
During its operation, the server reports errors, informational messages, and
decrypted datagrams, by emitting signals errorMessage(), warningMessage(),
infoMessage(), and datagramReceived(). These messages are logged by the server's
UI:
\snippet secureudpserver/mainwindow.cpp 0
*/

View File

@ -0,0 +1,85 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example threadedfortuneserver
\title Threaded Fortune Server
\examplecategory {Networking}
\meta tags {tcp,network,threading,server,synchronous-io}
\ingroup examples-network
\brief The Threaded Fortune Server example shows how to create a server for a
simple network service that uses threads to handle requests from different
clients. It is intended to be run alongside the Fortune Client example.
\image threadedfortuneserver-example.png
The implementation of this example is similar to that of the
\l{fortuneserver}{Fortune Server} example, but here we will
implement a subclass of QTcpServer that starts each connection in a
different thread.
For this we need two classes: FortuneServer, a QTcpServer subclass, and
FortuneThread, which inherits QThread.
\snippet threadedfortuneserver/fortuneserver.h 0
FortuneServer inherits QTcpServer and reimplements
QTcpServer::incomingConnection(). We also use it for storing the list of
random fortunes.
\snippet threadedfortuneserver/fortuneserver.cpp 0
We use FortuneServer's constructor to simply generate the list of
fortunes.
\snippet threadedfortuneserver/fortuneserver.cpp 1
Our implementation of QTcpServer::incomingConnection() creates a
FortuneThread object, passing the incoming socket descriptor and a random
fortune to FortuneThread's constructor. By connecting FortuneThread's
finished() signal to QObject::deleteLater(), we ensure that the thread
gets deleted once it has finished. We can then call QThread::start(),
which starts the thread.
\snippet threadedfortuneserver/fortunethread.h 0
Moving on to the FortuneThread class, this is a QThread subclass whose job
is to write the fortune to the connected socket. The class reimplements
QThread::run(), and it has a signal for reporting errors.
\snippet threadedfortuneserver/fortunethread.cpp 0
FortuneThread's constructor simply stores the socket descriptor and
fortune text, so that they are available for run() later on.
\snippet threadedfortuneserver/fortunethread.cpp 1
The first thing our run() function does is to create a QTcpSocket object
on the stack. What's worth noticing is that we are creating this object
inside the thread, which automatically associates the socket to the
thread's event loop. This ensures that Qt will not try to deliver events
to our socket from the main thread while we are accessing it from
FortuneThread::run().
\snippet threadedfortuneserver/fortunethread.cpp 2
The socket is initialized by calling QTcpSocket::setSocketDescriptor(),
passing our socket descriptor as an argument. We expect this to succeed,
but just to be sure, (although unlikely, the system may run out of
resources,) we catch the return value and report any error.
\snippet threadedfortuneserver/fortunethread.cpp 3
As with the \l{fortuneserver}{Fortune Server} example, we encode
the fortune into a QByteArray using QDataStream.
\snippet threadedfortuneserver/fortunethread.cpp 4
But unlike the previous example, we finish off by calling
QTcpSocket::waitForDisconnected(), which blocks the calling thread until
the socket has disconnected. Because we are running in a separate thread,
the GUI will remain responsive.
\sa {Fortune Server}, {Fortune Client}, {Blocking Fortune Client}
*/

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example torrent
\title Torrent Example
\ingroup examples-network
\brief Demonstrates complex TCP/IP operations.
This example demonstrates some of the complex TCP/IP operations
supported by the Qt Network APIs.
\image torrent-example.png
*/

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(fortuneclient LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/fortuneclient")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(fortuneclient
client.cpp client.h
main.cpp
)
set_target_properties(fortuneclient PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(fortuneclient PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS fortuneclient
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,176 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtNetwork>
#include "client.h"
//! [0]
Client::Client(QWidget *parent)
: QDialog(parent)
, hostCombo(new QComboBox)
, portLineEdit(new QLineEdit)
, getFortuneButton(new QPushButton(tr("Get Fortune")))
, tcpSocket(new QTcpSocket(this))
{
//! [0]
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 != QLatin1String("localhost"))
hostCombo->addItem(QString("localhost"));
// find out IP addresses of this machine
const QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// add non-localhost addresses
for (const QHostAddress &entry : ipAddressesList) {
if (!entry.isLoopback())
hostCombo->addItem(entry.toString());
}
// add localhost addresses
for (const QHostAddress &entry : ipAddressesList) {
if (entry.isLoopback())
hostCombo->addItem(entry.toString());
}
portLineEdit->setValidator(new QIntValidator(1, 65535, this));
auto hostLabel = new QLabel(tr("&Server name:"));
hostLabel->setBuddy(hostCombo);
auto portLabel = new QLabel(tr("S&erver port:"));
portLabel->setBuddy(portLineEdit);
statusLabel = new QLabel(tr("This examples requires that you run the "
"Fortune Server example as well."));
getFortuneButton->setDefault(true);
getFortuneButton->setEnabled(false);
auto quitButton = new QPushButton(tr("Quit"));
auto buttonBox = new QDialogButtonBox;
buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
//! [1]
in.setDevice(tcpSocket);
in.setVersion(QDataStream::Qt_6_5);
//! [1]
connect(hostCombo, &QComboBox::editTextChanged,
this, &Client::enableGetFortuneButton);
connect(portLineEdit, &QLineEdit::textChanged,
this, &Client::enableGetFortuneButton);
connect(getFortuneButton, &QAbstractButton::clicked,
this, &Client::requestNewFortune);
connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close);
//! [2] //! [3]
connect(tcpSocket, &QIODevice::readyRead, this, &Client::readFortune);
//! [2] //! [4]
connect(tcpSocket, &QAbstractSocket::errorOccurred,
//! [3]
this, &Client::displayError);
//! [4]
QGridLayout *mainLayout = nullptr;
if (QGuiApplication::styleHints()->showIsFullScreen() || QGuiApplication::styleHints()->showIsMaximized()) {
auto outerVerticalLayout = new QVBoxLayout(this);
outerVerticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
auto outerHorizontalLayout = new QHBoxLayout;
outerHorizontalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
auto groupBox = new QGroupBox(QGuiApplication::applicationDisplayName());
mainLayout = new QGridLayout(groupBox);
outerHorizontalLayout->addWidget(groupBox);
outerHorizontalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
outerVerticalLayout->addLayout(outerHorizontalLayout);
outerVerticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
} else {
mainLayout = new QGridLayout(this);
}
mainLayout->addWidget(hostLabel, 0, 0);
mainLayout->addWidget(hostCombo, 0, 1);
mainLayout->addWidget(portLabel, 1, 0);
mainLayout->addWidget(portLineEdit, 1, 1);
mainLayout->addWidget(statusLabel, 2, 0, 1, 2);
mainLayout->addWidget(buttonBox, 3, 0, 1, 2);
setWindowTitle(QGuiApplication::applicationDisplayName());
portLineEdit->setFocus();
//! [5]
}
//! [5]
//! [6]
void Client::requestNewFortune()
{
getFortuneButton->setEnabled(false);
tcpSocket->abort();
//! [7]
tcpSocket->connectToHost(hostCombo->currentText(),
portLineEdit->text().toInt());
//! [7]
}
//! [6]
//! [8]
void Client::readFortune()
{
in.startTransaction();
QString nextFortune;
in >> nextFortune;
if (!in.commitTransaction())
return;
if (nextFortune == currentFortune) {
QTimer::singleShot(0, this, &Client::requestNewFortune);
return;
}
currentFortune = nextFortune;
statusLabel->setText(currentFortune);
getFortuneButton->setEnabled(true);
}
//! [8]
//! [13]
void Client::displayError(QAbstractSocket::SocketError socketError)
{
switch (socketError) {
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(this, tr("Fortune Client"),
tr("The host was not found. Please check the "
"host name and port settings."));
break;
case QAbstractSocket::ConnectionRefusedError:
QMessageBox::information(this, tr("Fortune Client"),
tr("The connection was refused by the peer. "
"Make sure the fortune server is running, "
"and check that the host name and port "
"settings are correct."));
break;
default:
QMessageBox::information(this, tr("Fortune Client"),
tr("The following error occurred: %1.")
.arg(tcpSocket->errorString()));
}
getFortuneButton->setEnabled(true);
}
//! [13]
void Client::enableGetFortuneButton()
{
getFortuneButton->setEnabled(!hostCombo->currentText().isEmpty() &&
!portLineEdit->text().isEmpty());
}

View File

@ -0,0 +1,45 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CLIENT_H
#define CLIENT_H
#include <QDataStream>
#include <QDialog>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
class QComboBox;
class QLabel;
class QLineEdit;
class QPushButton;
class QTcpSocket;
QT_END_NAMESPACE
//! [0]
class Client : public QDialog
{
Q_OBJECT
public:
explicit Client(QWidget *parent = nullptr);
private slots:
void requestNewFortune();
void readFortune();
void displayError(QAbstractSocket::SocketError socketError);
void enableGetFortuneButton();
private:
QComboBox *hostCombo = nullptr;
QLineEdit *portLineEdit = nullptr;
QLabel *statusLabel = nullptr;
QPushButton *getFortuneButton = nullptr;
QTcpSocket *tcpSocket = nullptr;
QDataStream in;
QString currentFortune;
};
//! [0]
#endif

View File

@ -0,0 +1,10 @@
QT += network widgets
requires(qtConfig(combobox))
HEADERS = client.h
SOURCES = client.cpp \
main.cpp
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/fortuneclient
INSTALLS += target

View File

@ -0,0 +1,14 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "client.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QApplication::setApplicationDisplayName(Client::tr("Fortune Client"));
Client client;
client.show();
return app.exec();
}

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(fortuneserver LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/fortuneserver")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(fortuneserver
main.cpp
server.cpp server.h
)
set_target_properties(fortuneserver PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(fortuneserver PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS fortuneserver
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,9 @@
QT += network widgets
HEADERS = server.h
SOURCES = server.cpp \
main.cpp
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/fortuneserver
INSTALLS += target

View File

@ -0,0 +1,15 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "server.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QApplication::setApplicationDisplayName(Server::tr("Fortune Server"));
Server server;
server.show();
return app.exec();
}

View File

@ -0,0 +1,111 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtNetwork>
#include <QtCore>
#include "server.h"
Server::Server(QWidget *parent)
: QDialog(parent)
, statusLabel(new QLabel)
{
statusLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
initServer();
//! [2]
fortunes << tr("You've been leading a dog's life. Stay off the furniture.")
<< tr("You've got to think about tomorrow.")
<< tr("You will be surprised by a loud noise.")
<< tr("You will feel hungry again in another hour.")
<< tr("You might have mail.")
<< tr("You cannot kill time without injuring eternity.")
<< tr("Computers are not intelligent. They only think they are.");
//! [2]
auto quitButton = new QPushButton(tr("Quit"));
quitButton->setAutoDefault(false);
connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close);
//! [3]
connect(tcpServer, &QTcpServer::newConnection, this, &Server::sendFortune);
//! [3]
auto buttonLayout = new QHBoxLayout;
buttonLayout->addStretch(1);
buttonLayout->addWidget(quitButton);
buttonLayout->addStretch(1);
QVBoxLayout *mainLayout = nullptr;
if (QGuiApplication::styleHints()->showIsFullScreen() || QGuiApplication::styleHints()->showIsMaximized()) {
auto outerVerticalLayout = new QVBoxLayout(this);
outerVerticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
auto outerHorizontalLayout = new QHBoxLayout;
outerHorizontalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
auto groupBox = new QGroupBox(QGuiApplication::applicationDisplayName());
mainLayout = new QVBoxLayout(groupBox);
outerHorizontalLayout->addWidget(groupBox);
outerHorizontalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
outerVerticalLayout->addLayout(outerHorizontalLayout);
outerVerticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
} else {
mainLayout = new QVBoxLayout(this);
}
mainLayout->addWidget(statusLabel);
mainLayout->addLayout(buttonLayout);
setWindowTitle(QGuiApplication::applicationDisplayName());
}
void Server::initServer()
{
//! [0] //! [1]
tcpServer = new QTcpServer(this);
if (!tcpServer->listen()) {
QMessageBox::critical(this, tr("Fortune Server"),
tr("Unable to start the server: %1.")
.arg(tcpServer->errorString()));
close();
return;
}
//! [0]
QString ipAddress;
const QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// use the first non-localhost IPv4 address
for (const QHostAddress &entry : ipAddressesList) {
if (entry != QHostAddress::LocalHost && entry.toIPv4Address()) {
ipAddress = entry.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 Fortune Client example now.")
.arg(ipAddress).arg(tcpServer->serverPort()));
//! [1]
}
//! [4]
void Server::sendFortune()
{
//! [5]
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_6_5);
out << fortunes[QRandomGenerator::global()->bounded(fortunes.size())];
//! [4] //! [7]
QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
connect(clientConnection, &QAbstractSocket::disconnected,
clientConnection, &QObject::deleteLater);
//! [7] //! [8]
clientConnection->write(block);
clientConnection->disconnectFromHost();
//! [5]
}
//! [8]

View File

@ -0,0 +1,36 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SERVER_H
#define SERVER_H
#include <QDialog>
#include <QString>
#include <QList>
QT_BEGIN_NAMESPACE
class QLabel;
class QTcpServer;
QT_END_NAMESPACE
//! [0]
class Server : public QDialog
{
Q_OBJECT
public:
explicit Server(QWidget *parent = nullptr);
private slots:
void sendFortune();
private:
void initServer();
QLabel *statusLabel = nullptr;
QTcpServer *tcpServer = nullptr;
QList<QString> fortunes;
};
//! [0]
#endif

View File

@ -0,0 +1,39 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(http LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/http")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(http
authenticationdialog.ui
httpwindow.cpp httpwindow.h
main.cpp
)
set_target_properties(http PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(http PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS http
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,133 @@
<ui version="4.0" >
<class>Dialog</class>
<widget class="QDialog" name="Dialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>389</width>
<height>243</height>
</rect>
</property>
<property name="windowTitle" >
<string>Http authentication required</string>
</property>
<layout class="QGridLayout" >
<item row="0" column="0" colspan="2" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>You need to supply a Username and a Password to access this site</string>
</property>
<property name="wordWrap" >
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>Username:</string>
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="QLineEdit" name="userEdit" />
</item>
<item row="3" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>Password:</string>
</property>
</widget>
</item>
<item row="3" column="1" >
<widget class="QLineEdit" name="passwordEdit">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2" >
<widget class="QDialogButtonBox" name="buttonBox" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>Site:</string>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLabel" name="siteDescription" >
<property name="font" >
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text" >
<string>%1 at %2</string>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0" >
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel" >
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel" >
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,10 @@
QT += network widgets
HEADERS += httpwindow.h
SOURCES += httpwindow.cpp \
main.cpp
FORMS += authenticationdialog.ui
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/http
INSTALLS += target

View File

@ -0,0 +1,306 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "httpwindow.h"
#include "ui_authenticationdialog.h"
#include <QtWidgets>
#include <QtNetwork>
#include <QUrl>
#include <algorithm>
#include <memory>
#if QT_CONFIG(ssl)
const char defaultUrl[] = "https://www.qt.io/";
#else
const char defaultUrl[] = "http://www.qt.io/";
#endif
const char defaultFileName[] = "index.html";
ProgressDialog::ProgressDialog(const QUrl &url, QWidget *parent)
: QProgressDialog(parent)
{
setWindowTitle(tr("Download Progress"));
setLabelText(tr("Downloading %1.").arg(url.toDisplayString()));
setMinimum(0);
setValue(0);
setMinimumDuration(0);
setMinimumSize(QSize(400, 75));
}
void ProgressDialog::networkReplyProgress(qint64 bytesRead, qint64 totalBytes)
{
setMaximum(totalBytes);
setValue(bytesRead);
}
HttpWindow::HttpWindow(QWidget *parent)
: QDialog(parent)
, statusLabel(new QLabel(tr("Please enter the URL of a file you want to download.\n\n"), this))
, urlLineEdit(new QLineEdit(defaultUrl))
, downloadButton(new QPushButton(tr("Download")))
, launchCheckBox(new QCheckBox(tr("Launch file")))
, defaultFileLineEdit(new QLineEdit(defaultFileName))
, downloadDirectoryLineEdit(new QLineEdit)
{
setWindowTitle(tr("HTTP Client"));
//! [qnam-auth-required-1]
connect(&qnam, &QNetworkAccessManager::authenticationRequired,
this, &HttpWindow::slotAuthenticationRequired);
//! [qnam-auth-required-1]
#if QT_CONFIG(networkproxy)
connect(&qnam, &QNetworkAccessManager::proxyAuthenticationRequired,
this, &HttpWindow::slotProxyAuthenticationRequired);
#endif
QFormLayout *formLayout = new QFormLayout;
urlLineEdit->setClearButtonEnabled(true);
connect(urlLineEdit, &QLineEdit::textChanged, this, &HttpWindow::enableDownloadButton);
formLayout->addRow(tr("&URL:"), urlLineEdit);
QString downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
if (downloadDirectory.isEmpty() || !QFileInfo(downloadDirectory).isDir())
downloadDirectory = QDir::currentPath();
downloadDirectoryLineEdit->setText(QDir::toNativeSeparators(downloadDirectory));
formLayout->addRow(tr("&Download directory:"), downloadDirectoryLineEdit);
formLayout->addRow(tr("Default &file:"), defaultFileLineEdit);
launchCheckBox->setChecked(true);
formLayout->addRow(launchCheckBox);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(formLayout);
mainLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
statusLabel->setWordWrap(true);
mainLayout->addWidget(statusLabel);
downloadButton->setDefault(true);
connect(downloadButton, &QAbstractButton::clicked, this, &HttpWindow::downloadFile);
QPushButton *quitButton = new QPushButton(tr("Quit"));
quitButton->setAutoDefault(false);
connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close);
QDialogButtonBox *buttonBox = new QDialogButtonBox;
buttonBox->addButton(downloadButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
mainLayout->addWidget(buttonBox);
urlLineEdit->setFocus();
}
HttpWindow::~HttpWindow() = default;
void HttpWindow::startRequest(const QUrl &requestedUrl)
{
url = requestedUrl;
httpRequestAborted = false;
//! [qnam-download]
reply.reset(qnam.get(QNetworkRequest(url)));
//! [qnam-download]
//! [connecting-reply-to-slots]
connect(reply.get(), &QNetworkReply::finished, this, &HttpWindow::httpFinished);
//! [networkreply-readyread-1]
connect(reply.get(), &QIODevice::readyRead, this, &HttpWindow::httpReadyRead);
//! [networkreply-readyread-1]
#if QT_CONFIG(ssl)
//! [sslerrors-1]
connect(reply.get(), &QNetworkReply::sslErrors, this, &HttpWindow::sslErrors);
//! [sslerrors-1]
#endif
//! [connecting-reply-to-slots]
ProgressDialog *progressDialog = new ProgressDialog(url, this);
progressDialog->setAttribute(Qt::WA_DeleteOnClose);
connect(progressDialog, &QProgressDialog::canceled, this, &HttpWindow::cancelDownload);
connect(reply.get(), &QNetworkReply::downloadProgress,
progressDialog, &ProgressDialog::networkReplyProgress);
connect(reply.get(), &QNetworkReply::finished, progressDialog, &ProgressDialog::hide);
progressDialog->show();
statusLabel->setText(tr("Downloading %1...").arg(url.toString()));
}
void HttpWindow::downloadFile()
{
const QString urlSpec = urlLineEdit->text().trimmed();
if (urlSpec.isEmpty())
return;
const QUrl newUrl = QUrl::fromUserInput(urlSpec);
if (!newUrl.isValid()) {
QMessageBox::information(this, tr("Error"),
tr("Invalid URL: %1: %2").arg(urlSpec, newUrl.errorString()));
return;
}
QString fileName = newUrl.fileName();
if (fileName.isEmpty())
fileName = defaultFileLineEdit->text().trimmed();
if (fileName.isEmpty())
fileName = defaultFileName;
QString downloadDirectory = QDir::cleanPath(downloadDirectoryLineEdit->text().trimmed());
bool useDirectory = !downloadDirectory.isEmpty() && QFileInfo(downloadDirectory).isDir();
if (useDirectory)
fileName.prepend(downloadDirectory + '/');
if (QFile::exists(fileName)) {
QString alreadyExists = useDirectory
? tr("There already exists a file called %1. Overwrite?")
: tr("There already exists a file called %1 in the current directory. "
"Overwrite?");
QMessageBox::StandardButton response = QMessageBox::question(this,
tr("Overwrite Existing File"),
alreadyExists.arg(QDir::toNativeSeparators(fileName)),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (response == QMessageBox::No)
return;
QFile::remove(fileName);
}
file = openFileForWrite(fileName);
if (!file)
return;
downloadButton->setEnabled(false);
// schedule the request
startRequest(newUrl);
}
std::unique_ptr<QFile> HttpWindow::openFileForWrite(const QString &fileName)
{
std::unique_ptr<QFile> file = std::make_unique<QFile>(fileName);
if (!file->open(QIODevice::WriteOnly)) {
QMessageBox::information(this, tr("Error"),
tr("Unable to save the file %1: %2.")
.arg(QDir::toNativeSeparators(fileName),
file->errorString()));
return nullptr;
}
return file;
}
void HttpWindow::cancelDownload()
{
statusLabel->setText(tr("Download canceled."));
httpRequestAborted = true;
reply->abort();
downloadButton->setEnabled(true);
}
void HttpWindow::httpFinished()
{
QFileInfo fi;
if (file) {
fi.setFile(file->fileName());
file->close();
file.reset();
}
//! [networkreply-error-handling-1]
QNetworkReply::NetworkError error = reply->error();
const QString &errorString = reply->errorString();
//! [networkreply-error-handling-1]
reply.reset();
//! [networkreply-error-handling-2]
if (error != QNetworkReply::NoError) {
QFile::remove(fi.absoluteFilePath());
// For "request aborted" we handle the label and button in cancelDownload()
if (!httpRequestAborted) {
statusLabel->setText(tr("Download failed:\n%1.").arg(errorString));
downloadButton->setEnabled(true);
}
return;
}
//! [networkreply-error-handling-2]
statusLabel->setText(tr("Downloaded %1 bytes to %2\nin\n%3")
.arg(fi.size())
.arg(fi.fileName(), QDir::toNativeSeparators(fi.absolutePath())));
if (launchCheckBox->isChecked())
QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absoluteFilePath()));
downloadButton->setEnabled(true);
}
//! [networkreply-readyread-2]
void HttpWindow::httpReadyRead()
{
// This slot gets called every time the QNetworkReply has new data.
// We read all of its new data and write it into the file.
// That way we use less RAM than when reading it at the finished()
// signal of the QNetworkReply
if (file)
file->write(reply->readAll());
}
//! [networkreply-readyread-2]
void HttpWindow::enableDownloadButton()
{
downloadButton->setEnabled(!urlLineEdit->text().isEmpty());
}
//! [qnam-auth-required-2]
void HttpWindow::slotAuthenticationRequired(QNetworkReply *, QAuthenticator *authenticator)
{
QDialog authenticationDialog;
Ui::Dialog ui;
ui.setupUi(&authenticationDialog);
authenticationDialog.adjustSize();
ui.siteDescription->setText(tr("%1 at %2").arg(authenticator->realm(), url.host()));
// Did the URL have information? Fill the UI.
// This is only relevant if the URL-supplied credentials were wrong
ui.userEdit->setText(url.userName());
ui.passwordEdit->setText(url.password());
if (authenticationDialog.exec() == QDialog::Accepted) {
authenticator->setUser(ui.userEdit->text());
authenticator->setPassword(ui.passwordEdit->text());
}
}
//! [qnam-auth-required-2]
#if QT_CONFIG(ssl)
//! [sslerrors-2]
void HttpWindow::sslErrors(const QList<QSslError> &errors)
{
QString errorString;
for (const QSslError &error : errors) {
if (!errorString.isEmpty())
errorString += '\n';
errorString += error.errorString();
}
if (QMessageBox::warning(this, tr("TLS Errors"),
tr("One or more TLS errors has occurred:\n%1").arg(errorString),
QMessageBox::Ignore | QMessageBox::Abort)
== QMessageBox::Ignore) {
reply->ignoreSslErrors();
}
}
//! [sslerrors-2]
#endif
#if QT_CONFIG(networkproxy)
void HttpWindow::slotProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
{
QDialog authenticationDialog;
Ui::Dialog ui;
ui.setupUi(&authenticationDialog);
authenticationDialog.adjustSize();
ui.siteDescription->setText(tr("A network proxy at %1 is requesting credentials for realm: %2")
.arg(proxy.hostName(), authenticator->realm()));
// If the user passed credentials in the URL to http_proxy or similar they may be available to
// us. Otherwise this will just leave the fields empty
ui.userEdit->setText(proxy.user());
ui.passwordEdit->setText(proxy.password());
if (authenticationDialog.exec() == QDialog::Accepted) {
authenticator->setUser(ui.userEdit->text());
authenticator->setPassword(ui.passwordEdit->text());
}
}
#endif

View File

@ -0,0 +1,79 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef HTTPWINDOW_H
#define HTTPWINDOW_H
#include <QProgressDialog>
#include <QNetworkAccessManager>
#include <QUrl>
#include <memory>
QT_BEGIN_NAMESPACE
class QFile;
class QLabel;
class QLineEdit;
class QPushButton;
class QSslError;
class QAuthenticator;
class QNetworkReply;
class QCheckBox;
#if QT_CONFIG(networkproxy)
class QNetworkProxy;
#endif
QT_END_NAMESPACE
class ProgressDialog : public QProgressDialog {
Q_OBJECT
public:
explicit ProgressDialog(const QUrl &url, QWidget *parent = nullptr);
public slots:
void networkReplyProgress(qint64 bytesRead, qint64 totalBytes);
};
class HttpWindow : public QDialog
{
Q_OBJECT
public:
explicit HttpWindow(QWidget *parent = nullptr);
~HttpWindow();
void startRequest(const QUrl &requestedUrl);
private slots:
void downloadFile();
void cancelDownload();
void httpFinished();
void httpReadyRead();
void enableDownloadButton();
void slotAuthenticationRequired(QNetworkReply *, QAuthenticator *authenticator);
#if QT_CONFIG(ssl)
void sslErrors(const QList<QSslError> &errors);
#endif
#if QT_CONFIG(networkproxy)
void slotProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
#endif
private:
std::unique_ptr<QFile> openFileForWrite(const QString &fileName);
QLabel *statusLabel;
QLineEdit *urlLineEdit;
QPushButton *downloadButton;
QCheckBox *launchCheckBox;
QLineEdit *defaultFileLineEdit;
QLineEdit *downloadDirectoryLineEdit;
QUrl url;
QNetworkAccessManager qnam;
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> reply;
std::unique_ptr<QFile> file;
bool httpRequestAborted = false;
};
#endif

View File

@ -0,0 +1,20 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include <QDir>
#include <QScreen>
#include "httpwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
HttpWindow httpWin;
const QRect availableSize = httpWin.screen()->availableGeometry();
httpWin.resize(availableSize.width() / 5, availableSize.height() / 5);
httpWin.move((availableSize.width() - httpWin.width()) / 2, (availableSize.height() - httpWin.height()) / 2);
httpWin.show();
return app.exec();
}

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(multicastreceiver LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/multicastreceiver")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(multicastreceiver
main.cpp
receiver.cpp receiver.h
)
set_target_properties(multicastreceiver PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(multicastreceiver PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS multicastreceiver
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "receiver.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Receiver receiver;
receiver.show();
return app.exec();
}

View File

@ -0,0 +1,11 @@
QT += network widgets
requires(qtConfig(udpsocket))
HEADERS = receiver.h
SOURCES = receiver.cpp \
main.cpp
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/multicastreceiver
INSTALLS += target

View File

@ -0,0 +1,64 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtNetwork>
#include "receiver.h"
Receiver::Receiver(QWidget *parent)
: QDialog(parent),
groupAddress4(QStringLiteral("239.255.43.21")),
groupAddress6(QStringLiteral("ff12::2115"))
{
statusLabel = new QLabel(tr("Listening for multicast messages on both IPv4 and IPv6"));
auto quitButton = new QPushButton(tr("&Quit"));
auto buttonLayout = new QHBoxLayout;
buttonLayout->addStretch(1);
buttonLayout->addWidget(quitButton);
buttonLayout->addStretch(1);
auto mainLayout = new QVBoxLayout;
mainLayout->addWidget(statusLabel);
mainLayout->addLayout(buttonLayout);
setLayout(mainLayout);
setWindowTitle(tr("Multicast Receiver"));
udpSocket4.bind(QHostAddress::AnyIPv4, 45454, QUdpSocket::ShareAddress);
udpSocket4.joinMulticastGroup(groupAddress4);
if (!udpSocket6.bind(QHostAddress::AnyIPv6, 45454, QUdpSocket::ShareAddress) ||
!udpSocket6.joinMulticastGroup(groupAddress6))
statusLabel->setText(tr("Listening for multicast messages on IPv4 only"));
connect(&udpSocket4, &QUdpSocket::readyRead,
this, &Receiver::processPendingDatagrams);
connect(&udpSocket6, &QUdpSocket::readyRead,
this, &Receiver::processPendingDatagrams);
connect(quitButton, &QPushButton::clicked,
qApp, &QCoreApplication::quit);
}
void Receiver::processPendingDatagrams()
{
QByteArray datagram;
// using QUdpSocket::readDatagram (API since Qt 4)
while (udpSocket4.hasPendingDatagrams()) {
datagram.resize(qsizetype(udpSocket4.pendingDatagramSize()));
udpSocket4.readDatagram(datagram.data(), datagram.size());
statusLabel->setText(tr("Received IPv4 datagram: \"%1\"")
.arg(datagram.constData()));
}
// using QUdpSocket::receiveDatagram (API since Qt 5.8)
while (udpSocket6.hasPendingDatagrams()) {
QNetworkDatagram dgram = udpSocket6.receiveDatagram();
statusLabel->setText(statusLabel->text() +
tr("\nReceived IPv6 datagram from [%2]:%3: \"%1\"")
.arg(dgram.data().constData(), dgram.senderAddress().toString())
.arg(dgram.senderPort()));
}
}

View File

@ -0,0 +1,33 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QDialog>
#include <QHostAddress>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
class QLabel;
QT_END_NAMESPACE
class Receiver : public QDialog
{
Q_OBJECT
public:
explicit Receiver(QWidget *parent = nullptr);
private slots:
void processPendingDatagrams();
private:
QLabel *statusLabel = nullptr;
QUdpSocket udpSocket4;
QUdpSocket udpSocket6;
QHostAddress groupAddress4;
QHostAddress groupAddress6;
};
#endif

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(multicastsender LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/multicastsender")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(multicastsender
main.cpp
sender.cpp sender.h
)
set_target_properties(multicastsender PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(multicastsender PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS multicastsender
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "sender.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Sender sender;
sender.show();
return app.exec();
}

View File

@ -0,0 +1,10 @@
HEADERS = sender.h
SOURCES = sender.cpp \
main.cpp
QT += network widgets
requires(qtConfig(udpsocket))
# install
target.path = $$[QT_INSTALL_EXAMPLES]/network/multicastsender
INSTALLS += target

View File

@ -0,0 +1,72 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "sender.h"
Sender::Sender(QWidget *parent)
: QDialog(parent),
groupAddress4(QStringLiteral("239.255.43.21")),
groupAddress6(QStringLiteral("ff12::2115"))
{
// force binding to their respective families
udpSocket4.bind(QHostAddress(QHostAddress::AnyIPv4), 0);
udpSocket6.bind(QHostAddress(QHostAddress::AnyIPv6), udpSocket4.localPort());
QString msg = tr("Ready to multicast datagrams to groups %1 and [%2] on port 45454").arg(groupAddress4.toString());
if (udpSocket6.state() != QAbstractSocket::BoundState)
msg = tr("IPv6 failed. Ready to multicast datagrams to group %1 on port 45454").arg(groupAddress4.toString());
else
msg = msg.arg(groupAddress6.toString());
statusLabel = new QLabel(msg);
auto ttlLabel = new QLabel(tr("TTL for IPv4 multicast datagrams:"));
auto ttlSpinBox = new QSpinBox;
ttlSpinBox->setRange(0, 255);
auto ttlLayout = new QHBoxLayout;
ttlLayout->addWidget(ttlLabel);
ttlLayout->addWidget(ttlSpinBox);
startButton = new QPushButton(tr("&Start"));
auto quitButton = new QPushButton(tr("&Quit"));
auto buttonBox = new QDialogButtonBox;
buttonBox->addButton(startButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
connect(ttlSpinBox, &QSpinBox::valueChanged, this, &Sender::ttlChanged);
connect(startButton, &QPushButton::clicked, this, &Sender::startSending);
connect(quitButton, &QPushButton::clicked, this, &Sender::close);
connect(&timer, &QTimer::timeout, this, &Sender::sendDatagram);
auto mainLayout = new QVBoxLayout;
mainLayout->addWidget(statusLabel);
mainLayout->addLayout(ttlLayout);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setWindowTitle(tr("Multicast Sender"));
ttlSpinBox->setValue(1);
}
void Sender::ttlChanged(int newTtl)
{
// we only set the TTL on the IPv4 socket, as that changes the multicast scope
udpSocket4.setSocketOption(QAbstractSocket::MulticastTtlOption, newTtl);
}
void Sender::startSending()
{
startButton->setEnabled(false);
timer.start(1000);
}
void Sender::sendDatagram()
{
statusLabel->setText(tr("Now sending datagram %1").arg(messageNo));
QByteArray datagram = "Multicast message " + QByteArray::number(messageNo);
udpSocket4.writeDatagram(datagram, groupAddress4, 45454);
if (udpSocket6.state() == QAbstractSocket::BoundState)
udpSocket6.writeDatagram(datagram, groupAddress6, 45454);
++messageNo;
}

View File

@ -0,0 +1,34 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SENDER_H
#define SENDER_H
#include <QtWidgets>
#include <QtNetwork>
#include <QtCore>
class Sender : public QDialog
{
Q_OBJECT
public:
explicit Sender(QWidget *parent = nullptr);
private slots:
void ttlChanged(int newTtl);
void startSending();
void sendDatagram();
private:
QLabel *statusLabel = nullptr;
QPushButton *startButton = nullptr;
QUdpSocket udpSocket4;
QUdpSocket udpSocket6;
QTimer timer;
QHostAddress groupAddress4;
QHostAddress groupAddress6;
int messageNo = 1;
};
#endif

View File

@ -0,0 +1,42 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(multistreamclient LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/multistreamclient")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(multistreamclient
chatconsumer.cpp chatconsumer.h
client.cpp client.h
consumer.h
main.cpp
movieconsumer.cpp movieconsumer.h
timeconsumer.cpp timeconsumer.h
)
set_target_properties(multistreamclient PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(multistreamclient PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS multistreamclient
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,46 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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,32 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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() override;
void readDatagram(const QByteArray &ba) override;
private slots:
void returnPressed();
private:
QWidget *frameWidget;
QTextEdit *textEdit;
QLineEdit *lineEdit;
};
#endif

View File

@ -0,0 +1,171 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtNetwork>
#include "client.h"
#include "movieconsumer.h"
#include "timeconsumer.h"
#include "chatconsumer.h"
#include "../shared/sctpchannels.h"
Client::Client(QWidget *parent)
: QDialog(parent)
, consumers(SctpChannels::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[SctpChannels::Movie] = new MovieConsumer(this);
QLabel *timeLabel = new QLabel(tr("Time stream:"));
consumers[SctpChannels::Time] = new TimeConsumer(this);
QLabel *chatLabel = new QLabel(tr("&Chat:"));
consumers[SctpChannels::Chat] = new ChatConsumer(this);
chatLabel->setBuddy(consumers[SctpChannels::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, &QSctpSocket::errorOccurred, this, &Client::displayError);
connect(consumers[SctpChannels::Time], &Consumer::writeDatagram, this, &Client::writeDatagram);
connect(consumers[SctpChannels::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[SctpChannels::Movie]->widget(), 4, 0);
mainLayout->addWidget(consumers[SctpChannels::Time]->widget(), 4, 1);
mainLayout->addWidget(chatLabel, 5, 0);
mainLayout->addWidget(consumers[SctpChannels::Chat]->widget(), 6, 0, 1, 2);
setLayout(mainLayout);
portLineEdit->setFocus();
}
Client::~Client()
{
delete sctpSocket;
}
void Client::connected()
{
consumers[SctpChannels::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()));
}
enableConnectButton();
}
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,45 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CLIENT_H
#define CLIENT_H
#include <QDialog>
#include <QList>
#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:
QList<Consumer *> consumers;
QSctpSocket *sctpSocket;
QComboBox *hostCombo;
QLineEdit *portLineEdit;
QPushButton *connectButton;
};
#endif

View File

@ -0,0 +1,28 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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,13 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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,36 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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,27 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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() override;
void readDatagram(const QByteArray &ba) override;
void serverDisconnected() 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,47 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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, QIODeviceBase::WriteOnly);
ds << lastTime;
emit writeDatagram(buf);
}
void TimeConsumer::serverDisconnected()
{
lcdNumber->display(QLatin1String("--:--:--"));
}

View File

@ -0,0 +1,32 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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() override;
void readDatagram(const QByteArray &ba) override;
void serverDisconnected() override;
private slots:
void timerTick();
private:
QTime lastTime;
QLCDNumber *lcdNumber;
};
#endif

View File

@ -0,0 +1,42 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(multistreamserver LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/multistreamserver")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(multistreamserver
chatprovider.cpp chatprovider.h
main.cpp
movieprovider.cpp movieprovider.h
provider.h
server.cpp server.h
timeprovider.cpp timeprovider.h
)
set_target_properties(multistreamserver PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(multistreamserver PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS multistreamserver
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -0,0 +1,30 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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,20 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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) override;
void newConnection(QSctpSocket &client) override;
void clientDisconnected(QSctpSocket &client) override;
};
#endif

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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,26 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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,26 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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,18 @@
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

View File

@ -0,0 +1,29 @@
// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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,124 @@
// Copyright (C) 2015 Alex Trotsenko <alex1973tr@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtNetwork>
#include <QtAlgorithms>
#include "server.h"
#include "movieprovider.h"
#include "timeprovider.h"
#include "chatprovider.h"
#include "../shared/sctpchannels.h"
Server::Server(QWidget *parent)
: QDialog(parent)
, providers(SctpChannels::NumberOfChannels)
{
setWindowTitle(tr("Multi-stream Server"));
sctpServer = new QSctpServer(this);
sctpServer->setMaximumChannelCount(NumberOfChannels);
statusLabel = new QLabel;
QPushButton *quitButton = new QPushButton(tr("Quit"));
providers[SctpChannels::Movie] = new MovieProvider(this);
providers[SctpChannels::Time] = new TimeProvider(this);
providers[SctpChannels::Chat] = new ChatProvider(this);
connect(sctpServer, &QSctpServer::newConnection, this, &Server::newConnection);
connect(quitButton, &QPushButton::clicked, this, &Server::accept);
connect(providers[SctpChannels::Movie], &Provider::writeDatagram, this, &Server::writeDatagram);
connect(providers[SctpChannels::Time], &Provider::writeDatagram, this, &Server::writeDatagram);
connect(providers[SctpChannels::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);
}
}

Some files were not shown because too many files have changed in this diff Show More