diff options
Diffstat (limited to 'examples/network/network-chat')
-rw-r--r-- | examples/network/network-chat/CMakeLists.txt | 38 | ||||
-rw-r--r-- | examples/network/network-chat/chatdialog.cpp | 10 | ||||
-rw-r--r-- | examples/network/network-chat/chatdialog.h | 2 | ||||
-rw-r--r-- | examples/network/network-chat/client.cpp | 52 | ||||
-rw-r--r-- | examples/network/network-chat/client.h | 8 | ||||
-rw-r--r-- | examples/network/network-chat/connection.cpp | 57 | ||||
-rw-r--r-- | examples/network/network-chat/connection.h | 25 | ||||
-rw-r--r-- | examples/network/network-chat/main.cpp | 4 | ||||
-rw-r--r-- | examples/network/network-chat/network-chat.pro | 1 | ||||
-rw-r--r-- | examples/network/network-chat/peermanager.cpp | 46 | ||||
-rw-r--r-- | examples/network/network-chat/peermanager.h | 8 | ||||
-rw-r--r-- | examples/network/network-chat/server.cpp | 2 | ||||
-rw-r--r-- | examples/network/network-chat/server.h | 2 |
13 files changed, 142 insertions, 113 deletions
diff --git a/examples/network/network-chat/CMakeLists.txt b/examples/network/network-chat/CMakeLists.txt index 7e40564d33..127a4c2fb5 100644 --- a/examples/network/network-chat/CMakeLists.txt +++ b/examples/network/network-chat/CMakeLists.txt @@ -4,17 +4,10 @@ cmake_minimum_required(VERSION 3.16) project(network-chat LANGUAGES CXX) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") -endif() - -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/network-chat") - find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets) +qt_standard_project_setup() + qt_add_executable(network-chat chatdialog.cpp chatdialog.h chatdialog.ui client.cpp client.h @@ -29,15 +22,26 @@ set_target_properties(network-chat PROPERTIES MACOSX_BUNDLE TRUE ) -target_link_libraries(network-chat PUBLIC - Qt::Core - Qt::Gui - Qt::Network - Qt::Widgets +target_compile_definitions(network-chat PRIVATE + QT_USE_QSTRINGBUILDER +) + +target_link_libraries(network-chat PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Network + Qt6::Widgets ) install(TARGETS network-chat - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET network-chat + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR ) +install(SCRIPT ${deploy_script}) diff --git a/examples/network/network-chat/chatdialog.cpp b/examples/network/network-chat/chatdialog.cpp index ddec5c2997..55c98fed0c 100644 --- a/examples/network/network-chat/chatdialog.cpp +++ b/examples/network/network-chat/chatdialog.cpp @@ -1,10 +1,14 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -#include <QtWidgets> - #include "chatdialog.h" +#include <QTimer> +#include <QScrollBar> +#include <QLineEdit> +#include <QTextTable> +#include <QMessageBox> + ChatDialog::ChatDialog(QWidget *parent) : QDialog(parent) { @@ -27,7 +31,7 @@ ChatDialog::ChatDialog(QWidget *parent) myNickName = client.nickName(); newParticipant(myNickName); tableFormat.setBorder(0); - QTimer::singleShot(10 * 1000, this, SLOT(showInformation())); + QTimer::singleShot(10 * 1000, this, &ChatDialog::showInformation); } void ChatDialog::appendMessage(const QString &from, const QString &message) diff --git a/examples/network/network-chat/chatdialog.h b/examples/network/network-chat/chatdialog.h index adda73fb72..45a9858822 100644 --- a/examples/network/network-chat/chatdialog.h +++ b/examples/network/network-chat/chatdialog.h @@ -12,7 +12,7 @@ class ChatDialog : public QDialog, private Ui::ChatDialog Q_OBJECT public: - ChatDialog(QWidget *parent = nullptr); + explicit ChatDialog(QWidget *parent = nullptr); public slots: void appendMessage(const QString &from, const QString &message); diff --git a/examples/network/network-chat/client.cpp b/examples/network/network-chat/client.cpp index c9b165f724..c637224127 100644 --- a/examples/network/network-chat/client.cpp +++ b/examples/network/network-chat/client.cpp @@ -1,15 +1,18 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -#include <QtNetwork> - #include "client.h" #include "connection.h" #include "peermanager.h" +#include <QHostInfo> + +#include <algorithm> +#include <functional> + Client::Client() + : peerManager(new PeerManager(this)) { - peerManager = new PeerManager(this); peerManager->setServerPort(server.serverPort()); peerManager->startBroadcasting(); @@ -24,7 +27,7 @@ void Client::sendMessage(const QString &message) if (message.isEmpty()) return; - for (Connection *connection : qAsConst(peers)) + for (Connection *connection : std::as_const(peers)) connection->sendMessage(message); } @@ -34,43 +37,35 @@ QString Client::nickName() const + ':' + QString::number(server.serverPort()); } -bool Client::hasConnection(const QHostAddress &senderIp, int senderPort) const +bool Client::hasConnection(const QByteArray &peerUniqueId) const { - if (senderPort == -1) - return peers.contains(senderIp); - - if (!peers.contains(senderIp)) - return false; - - const QList<Connection *> connections = peers.values(senderIp); - for (const Connection *connection : connections) { - if (connection->peerPort() == senderPort) - return true; - } - - return false; + return peers.contains(peerUniqueId); } void Client::newConnection(Connection *connection) { - connection->setGreetingMessage(peerManager->userName()); - - connect(connection, &Connection::errorOccurred, this, &Client::connectionError); - connect(connection, &Connection::disconnected, this, &Client::disconnected); + connection->setGreetingMessage(peerManager->userName(), peerManager->uniqueId()); connect(connection, &Connection::readyForUse, this, &Client::readyForUse); + connect(connection, &Connection::errorOccurred, connection, &QObject::deleteLater); + connect(connection, &Connection::disconnected, connection, &QObject::deleteLater); } void Client::readyForUse() { Connection *connection = qobject_cast<Connection *>(sender()); - if (!connection || hasConnection(connection->peerAddress(), - connection->peerPort())) + if (!connection || hasConnection(connection->uniqueId())) { + if (connection) { + connection->disconnectFromHost(); + connection->deleteLater(); + } return; + } - connect(connection, &Connection::newMessage, - this, &Client::newMessage); + connect(connection, &Connection::errorOccurred, this, &Client::connectionError); + connect(connection, &Connection::disconnected, this, &Client::disconnected); + connect(connection, &Connection::newMessage, this, &Client::newMessage); - peers.insert(connection->peerAddress(), connection); + peers.insert(connection->uniqueId(), connection); QString nick = connection->name(); if (!nick.isEmpty()) emit newParticipant(nick); @@ -90,8 +85,7 @@ void Client::connectionError(QAbstractSocket::SocketError /* socketError */) void Client::removeConnection(Connection *connection) { - if (peers.contains(connection->peerAddress())) { - peers.remove(connection->peerAddress()); + if (peers.remove(connection->uniqueId())) { QString nick = connection->name(); if (!nick.isEmpty()) emit participantLeft(nick); diff --git a/examples/network/network-chat/client.h b/examples/network/network-chat/client.h index 5d55bf53d3..a09d0c21f6 100644 --- a/examples/network/network-chat/client.h +++ b/examples/network/network-chat/client.h @@ -4,12 +4,12 @@ #ifndef CLIENT_H #define CLIENT_H +#include "server.h" + #include <QAbstractSocket> #include <QHash> #include <QHostAddress> -#include "server.h" - class PeerManager; class Client : public QObject @@ -21,7 +21,7 @@ public: void sendMessage(const QString &message); QString nickName() const; - bool hasConnection(const QHostAddress &senderIp, int senderPort = -1) const; + bool hasConnection(const QByteArray &peerUniqueId) const; signals: void newMessage(const QString &from, const QString &message); @@ -39,7 +39,7 @@ private: PeerManager *peerManager; Server server; - QMultiHash<QHostAddress, Connection *> peers; + QHash<QByteArray, Connection *> peers; }; #endif diff --git a/examples/network/network-chat/connection.cpp b/examples/network/network-chat/connection.cpp index d89266a53c..0f59cc91ed 100644 --- a/examples/network/network-chat/connection.cpp +++ b/examples/network/network-chat/connection.cpp @@ -4,7 +4,7 @@ #include "connection.h" -#include <QtNetwork> +#include <QTimerEvent> static const int TransferTimeout = 30 * 1000; static const int PongTimeout = 60 * 1000; @@ -21,18 +21,12 @@ static const int PingInterval = 5 * 1000; * plaintext = { 0 => text } * ping = { 1 => null } * pong = { 2 => null } - * greeting = { 3 => text } + * greeting = { 3 => { text, bytes } } */ Connection::Connection(QObject *parent) : QTcpSocket(parent), writer(this) { - greetingMessage = tr("undefined"); - username = tr("unknown"); - state = WaitingForGreeting; - currentDataType = Undefined; - transferTimerId = -1; - isGreetingMessageSent = false; pingTimer.setInterval(PingInterval); connect(this, &QTcpSocket::readyRead, this, @@ -54,7 +48,7 @@ Connection::Connection(qintptr socketDescriptor, QObject *parent) Connection::~Connection() { - if (isGreetingMessageSent) { + if (isGreetingMessageSent && QAbstractSocket::state() != QAbstractSocket::UnconnectedState) { // Indicate clean shutdown. writer.endArray(); waitForBytesWritten(2000); @@ -66,9 +60,15 @@ QString Connection::name() const return username; } -void Connection::setGreetingMessage(const QString &message) +void Connection::setGreetingMessage(const QString &message, const QByteArray &uniqueId) { greetingMessage = message; + localUniqueId = uniqueId; +} + +QByteArray Connection::uniqueId() const +{ + return peerUniqueId; } bool Connection::sendMessage(const QString &message) @@ -124,7 +124,29 @@ void Connection::processReadyRead() reader.next(); } else { // Current state: read command payload - if (reader.isString()) { + if (currentDataType == Greeting) { + if (state == ReadingGreeting) { + if (!reader.isContainer() || !reader.isLengthKnown() || reader.length() != 2) + break; // protocol error + state = ProcessingGreeting; + reader.enterContainer(); + } + if (state != ProcessingGreeting) + break; // protocol error + if (reader.isString()) { + auto r = reader.readString(); + buffer += r.data; + } else if (reader.isByteArray()) { + auto r = reader.readByteArray(); + peerUniqueId += r.data; + if (r.status == QCborStreamReader::EndOfString) { + reader.leaveContainer(); + processGreeting(); + } + } + if (state == ProcessingGreeting) + continue; + } else if (reader.isString()) { auto r = reader.readString(); buffer += r.data; if (r.status != QCborStreamReader::EndOfString) @@ -132,7 +154,7 @@ void Connection::processReadyRead() } else if (reader.isNull()) { reader.next(); } else { - break; // protocol error + break; // protocol error } // Next state: no command read @@ -142,13 +164,7 @@ void Connection::processReadyRead() transferTimerId = -1; } - if (state == ReadingGreeting) { - if (currentDataType != Greeting) - break; // protocol error - processGreeting(); - } else { - processData(); - } + processData(); } } @@ -178,7 +194,10 @@ void Connection::sendGreetingMessage() writer.startMap(1); writer.append(Greeting); + writer.startArray(2); writer.append(greetingMessage); + writer.append(localUniqueId); + writer.endArray(); writer.endMap(); isGreetingMessageSent = true; diff --git a/examples/network/network-chat/connection.h b/examples/network/network-chat/connection.h index a3082247cb..4b063e7def 100644 --- a/examples/network/network-chat/connection.h +++ b/examples/network/network-chat/connection.h @@ -12,8 +12,6 @@ #include <QTcpSocket> #include <QTimer> -static const int MaxBufferSize = 1024000; - class Connection : public QTcpSocket { Q_OBJECT @@ -22,6 +20,7 @@ public: enum ConnectionState { WaitingForGreeting, ReadingGreeting, + ProcessingGreeting, ReadyForUse }; enum DataType { @@ -32,14 +31,16 @@ public: Undefined }; - Connection(QObject *parent = nullptr); - Connection(qintptr socketDescriptor, QObject *parent = nullptr); + explicit Connection(QObject *parent = nullptr); + explicit Connection(qintptr socketDescriptor, QObject *parent = nullptr); ~Connection(); QString name() const; - void setGreetingMessage(const QString &message); + void setGreetingMessage(const QString &message, const QByteArray &uniqueId); bool sendMessage(const QString &message); + QByteArray uniqueId() const; + signals: void readyForUse(); void newMessage(const QString &from, const QString &message); @@ -59,15 +60,17 @@ private: QCborStreamReader reader; QCborStreamWriter writer; - QString greetingMessage; - QString username; + QString greetingMessage = tr("undefined"); + QString username = tr("unknown"); QTimer pingTimer; QElapsedTimer pongTime; QString buffer; - ConnectionState state; - DataType currentDataType; - int transferTimerId; - bool isGreetingMessageSent; + QByteArray localUniqueId; + QByteArray peerUniqueId; + ConnectionState state = WaitingForGreeting; + DataType currentDataType = Undefined; + int transferTimerId = -1; + bool isGreetingMessageSent = false; }; #endif diff --git a/examples/network/network-chat/main.cpp b/examples/network/network-chat/main.cpp index bc6c4ef636..5d679d284b 100644 --- a/examples/network/network-chat/main.cpp +++ b/examples/network/network-chat/main.cpp @@ -1,11 +1,9 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -#include <QApplication> - #include "chatdialog.h" -#include <QtCore/QSettings> +#include <QtWidgets/QApplication> int main(int argc, char *argv[]) { diff --git a/examples/network/network-chat/network-chat.pro b/examples/network/network-chat/network-chat.pro index 2e3cbbc489..626a710949 100644 --- a/examples/network/network-chat/network-chat.pro +++ b/examples/network/network-chat/network-chat.pro @@ -11,6 +11,7 @@ SOURCES = chatdialog.cpp \ server.cpp FORMS = chatdialog.ui QT += network widgets +DEFINES += QT_USE_QSTRINGBUILDER requires(qtConfig(udpsocket)) requires(qtConfig(listwidget)) diff --git a/examples/network/network-chat/peermanager.cpp b/examples/network/network-chat/peermanager.cpp index 68a10b34be..a70ed7da73 100644 --- a/examples/network/network-chat/peermanager.cpp +++ b/examples/network/network-chat/peermanager.cpp @@ -2,20 +2,19 @@ // Copyright (C) 2018 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -#include <QtNetwork> - #include "client.h" #include "connection.h" #include "peermanager.h" +#include <QNetworkInterface> +#include <QUuid> + static const qint32 BroadcastInterval = 2000; static const unsigned broadcastPort = 45000; PeerManager::PeerManager(Client *client) - : QObject(client) + : QObject(client), client(client) { - this->client = client; - static const char *envVariables[] = { "USERNAME", "USER", "USERDOMAIN", "HOSTNAME", "DOMAINNAME" }; @@ -29,8 +28,12 @@ PeerManager::PeerManager(Client *client) if (username.isEmpty()) username = "unknown"; + // We generate a unique per-process identifier so we can avoid multiple + // connections to/from the same remote peer as well as ignore our own + // broadcasts. + localUniqueId = QUuid::createUuid().toByteArray(); + updateAddresses(); - serverPort = 0; broadcastSocket.bind(QHostAddress::Any, broadcastPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint); @@ -52,6 +55,11 @@ QString PeerManager::userName() const return username; } +QByteArray PeerManager::uniqueId() const +{ + return localUniqueId; +} + void PeerManager::startBroadcasting() { broadcastTimer.start(); @@ -59,11 +67,7 @@ void PeerManager::startBroadcasting() bool PeerManager::isLocalHostAddress(const QHostAddress &address) const { - for (const QHostAddress &localAddress : ipAddresses) { - if (address.isEqual(localAddress)) - return true; - } - return false; + return ipAddresses.contains(address); } void PeerManager::sendBroadcastDatagram() @@ -72,15 +76,14 @@ void PeerManager::sendBroadcastDatagram() { QCborStreamWriter writer(&datagram); writer.startArray(2); - writer.append(username); + writer.append(localUniqueId); writer.append(serverPort); writer.endArray(); } bool validBroadcastAddresses = true; - for (const QHostAddress &address : qAsConst(broadcastAddresses)) { - if (broadcastSocket.writeDatagram(datagram, address, - broadcastPort) == -1) + for (const QHostAddress &address : std::as_const(broadcastAddresses)) { + if (broadcastSocket.writeDatagram(datagram, address, broadcastPort) == -1) validBroadcastAddresses = false; } @@ -100,6 +103,7 @@ void PeerManager::readBroadcastDatagram() continue; int senderServerPort; + QByteArray peerUniqueId; { // decode the datagram QCborStreamReader reader(datagram); @@ -109,10 +113,12 @@ void PeerManager::readBroadcastDatagram() continue; reader.enterContainer(); - if (reader.lastError() != QCborError::NoError || !reader.isString()) + if (reader.lastError() != QCborError::NoError || !reader.isByteArray()) continue; - while (reader.readString().status == QCborStreamReader::Ok) { - // we don't actually need the username right now + auto r = reader.readByteArray(); + while (r.status == QCborStreamReader::Ok) { + peerUniqueId = r.data; + r = reader.readByteArray(); } if (reader.lastError() != QCborError::NoError || !reader.isUnsignedInteger()) @@ -120,10 +126,10 @@ void PeerManager::readBroadcastDatagram() senderServerPort = reader.toInteger(); } - if (isLocalHostAddress(senderIp) && senderServerPort == serverPort) + if (peerUniqueId == localUniqueId) continue; - if (!client->hasConnection(senderIp)) { + if (!client->hasConnection(peerUniqueId)) { Connection *connection = new Connection(this); emit newConnection(connection); connection->connectToHost(senderIp, senderServerPort); diff --git a/examples/network/network-chat/peermanager.h b/examples/network/network-chat/peermanager.h index b9ea8053b2..fff48540ea 100644 --- a/examples/network/network-chat/peermanager.h +++ b/examples/network/network-chat/peermanager.h @@ -18,10 +18,11 @@ class PeerManager : public QObject Q_OBJECT public: - PeerManager(Client *client); + explicit PeerManager(Client *client); void setServerPort(int port); QString userName() const; + QByteArray uniqueId() const; void startBroadcasting(); bool isLocalHostAddress(const QHostAddress &address) const; @@ -35,13 +36,14 @@ private slots: private: void updateAddresses(); - Client *client; + Client *client = nullptr; QList<QHostAddress> broadcastAddresses; QList<QHostAddress> ipAddresses; QUdpSocket broadcastSocket; QTimer broadcastTimer; QString username; - int serverPort; + QByteArray localUniqueId; + int serverPort = 0; }; #endif diff --git a/examples/network/network-chat/server.cpp b/examples/network/network-chat/server.cpp index 1537cbb06a..afc96717ca 100644 --- a/examples/network/network-chat/server.cpp +++ b/examples/network/network-chat/server.cpp @@ -1,8 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -#include <QtNetwork> - #include "connection.h" #include "server.h" diff --git a/examples/network/network-chat/server.h b/examples/network/network-chat/server.h index f41ffebaef..0557de30de 100644 --- a/examples/network/network-chat/server.h +++ b/examples/network/network-chat/server.h @@ -13,7 +13,7 @@ class Server : public QTcpServer Q_OBJECT public: - Server(QObject *parent = nullptr); + explicit Server(QObject *parent = nullptr); signals: void newConnection(Connection *connection); |