Qt5 Tutorial QTcpServer Loopback Dialog - 2020
In this tutorial, we will learn how to setup Client and Server using QTcpServer. This is a reconstructed version of Loopback Example.
Client requests a connection, and sends some bytes when the loopback server accepts the request. We'll use Gui Application displaying the progress of the byte transfer with additional buttons for start and quit.
For TCP Socket in general, please visit my C++ Tutorials: Socket - Server and Client.
We'll start with Qt Gui Application.
Qt->Qt Gui Application:
First, we need to add network module to our project file, LoopBackExample.pro:
QT += core gui QT += network greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = LoopBackExample TEMPLATE = app SOURCES += main.cpp\ dialog.cpp HEADERS += dialog.h FORMS += dialog.ui
UI components should look like this:
Then, we need to setup slots in dialog.h header:
public slots: // start button void startListening(); // request button void requestConnection(); // server accepts a request from a client void acceptConnection(); // client starts sending at the acceptance of a connection request void clientSendMessageToServer(); // slot for server progress bar void updateServerProgress(); // slot for client progress bar void updateClientProgress(qint64 numBytes);
Making connections between signals and slots.
For startListening button, since we do not have startListening() slot, we need to add it to the slot list, and then make the connection.
- When the dialog opens up, the "Start Listening" button is enabled while the "Request Connection" button is disabled.
- At "Start Listening" button click, the code calls startListening(). The "Request Connection" button is enabled to tell the client that the server is ready and listening for a connection, also the server label is switched to "Listening" from "Ready".
ui->startButton->setEnabled(false); ui->requestButton->setEnabled(true); ui->serverStatusLabel->setText(tr("Listening"));
- When the "Request Connection" button is clicked, in the requestConnection() function, the client calls
tcpClient.connectToHost(QHostAddress::LocalHost, tcpServer.serverPort());
This will emit the newConnection() signal. Actually, it is emitted each time a client connects to the server. - The acceptConnection() is the slot for the newConnection() signal.
- In the acceptConnection(), the call to nextPendingConnection() accepts the pending connection as a connected QTcpSocket, and returns a pointer to the QTcpSocket:
tcpServerConnection = tcpServer.nextPendingConnection();
- As soon as the server accepts the connection, the connected() signal is emitted which in turn calls its slot, clientSendMessageToServer().
- The bytesWritten() signal is emitted every time a payload of data has been written to the device (socket).
- The solot for the bytesWritten() is the updateClientProgress() which will update the client's progress bar.
connect(&tcpClient, SIGNAL(bytesWritten(qint64)), this, SLOT(updateClientProgress(qint64)));
- Also, the readyRead() signal is emitted once a new payload of network data has arrived on the network socket. In this example, it is the message that the client sent. Then its receiver slot function updateServerProgress() will be called and updates the progress bar:
connect(tcpServerConnection, SIGNAL(readyRead()), this, SLOT(updateServerProgress()));
- Then, close().
- Hello World
- Signals and Slots
- Q_OBJECT Macro
- MainWindow and Action
- MainWindow and ImageViewer using Designer A
- MainWindow and ImageViewer using Designer B
- Layouts
- Layouts without Designer
- Grid Layouts
- Splitter
- QDir
- QFile (Basic)
- Resource Files (.qrc)
- QComboBox
- QListWidget
- QTreeWidget
- QAction and Icon Resources
- QStatusBar
- QMessageBox
- QTimer
- QList
- QListIterator
- QMutableListIterator
- QLinkedList
- QMap
- QHash
- QStringList
- QTextStream
- QMimeType and QMimeDatabase
- QFile (Serialization I)
- QFile (Serialization II - Class)
- Tool Tips in HTML Style and with Resource Images
- QPainter
- QBrush and QRect
- QPainterPath and QPolygon
- QPen and Cap Style
- QBrush and QGradient
- QPainter and Transformations
- QGraphicsView and QGraphicsScene
- Customizing Items by inheriting QGraphicsItem
- QGraphicsView Animation
- FFmpeg Converter using QProcess
- QProgress Dialog - Modal and Modeless
- QVariant and QMetaType
- QtXML - Writing to a file
- QtXML - QtXML DOM Reading
- QThreads - Introduction
- QThreads - Creating Threads
- Creating QThreads using QtConcurrent
- QThreads - Priority
- QThreads - QMutex
- QThreads - GuiThread
- QtConcurrent QProgressDialog with QFutureWatcher
- QSemaphores - Producer and Consumer
- QThreads - wait()
- MVC - ModelView with QListView and QStringListModel
- MVC - ModelView with QTreeView and QDirModel
- MVC - ModelView with QTreeView and QFileSystemModel
- MVC - ModelView with QTableView and QItemDelegate
- QHttp - Downloading Files
- QNetworkAccessManager and QNetworkRequest - Downloading Files
- Qt's Network Download Example - Reconstructed
- QNetworkAccessManager - Downloading Files with UI and QProgressDialog
- QUdpSocket
- QTcpSocket
- QTcpSocket with Signals and Slots
- QTcpServer - Client and Server
- QTcpServer - Loopback Dialog
- QTcpServer - Client and Server using MultiThreading
- QTcpServer - Client and Server using QThreadPool
- Asynchronous QTcpServer - Client and Server using QThreadPool
- Qt Quick2 QML Animation - A
- Qt Quick2 QML Animation - B
- Short note on Ubuntu Install
- OpenGL with QT5
- Qt5 Webkit : Web Browser with QtCreator using QWebView Part A
- Qt5 Webkit : Web Browser with QtCreator using QWebView Part B
- Video Player with HTML5 QWebView and FFmpeg Converter
- Qt5 Add-in and Visual Studio 2012
- Qt5.3 Installation on Ubuntu 14.04
- Qt5.5 Installation on Ubuntu 14.04
- Short note on deploying to Windows
Run time.
main.cpp.
#include "dialog.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); Dialog w; w.setWindowTitle("Loopback Dialog Example"); w.show(); return a.exec(); }
dialog.h.
#ifndef DIALOG_H #define DIALOG_H #include <QDialog> #include <QTcpServer> #include <QTcpSocket> #include <QPushButton> #include <QLabel> #include <QProgressBar> #include <QMessageBox> namespace Ui { class Dialog; } class Dialog : public QDialog { Q_OBJECT public: explicit Dialog(QWidget *parent = 0); ~Dialog(); public slots: // start button void startListening(); // request button void requestConnection(); // server accepts a request from a client void acceptConnection(); // client starts sending at the acceptance of a connection request void clientSendMessageToServer(); // slot for server progress bar void updateServerProgress(); // slot for client progress bar void updateClientProgress(qint64 numBytes); void displayError(QAbstractSocket::SocketError socketError); private: Ui::Dialog *ui; // QTcpServer class provides a TCP-based server. QTcpServer tcpServer; QTcpSocket tcpClient; QTcpSocket *tcpServerConnection; int bytesToWrite; int bytesWritten; int bytesReceived; }; #endif // DIALOG_H
dialog.cpp.
#include "dialog.h" #include "ui_dialog.h" #include <QDebug> static const int TotalBytes = 50 * 1024 * 1024; static const int PayloadSize = 64 * 1024; // 64 KB Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { ui->setupUi(this); // request button - initially disabled ui->requestButton->setEnabled(false); connect(&tcpServer, SIGNAL(newConnection()), this, SLOT(acceptConnection())); connect(&tcpClient, SIGNAL(connected()), this, SLOT(clientSendMessageToServer())); connect(&tcpClient, SIGNAL(bytesWritten(qint64)), this, SLOT(updateClientProgress(qint64))); connect(&tcpClient, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError))); } Dialog::~Dialog() { delete ui; } // "Start Linstening" button clicked void Dialog::startListening() { ui->startButton->setEnabled(false); bytesWritten = 0; bytesReceived = 0; while (!tcpServer.isListening() && !tcpServer.listen()) { QMessageBox::StandardButton ret = QMessageBox::critical(this, tr("Loopback"), tr("Unable to start the test: %1.") .arg(tcpServer.errorString()), QMessageBox::Retry | QMessageBox::Cancel); if (ret == QMessageBox::Cancel) return; } ui->serverStatusLabel->setText(tr("Listening")); ui->requestButton->setEnabled(true); } void Dialog::requestConnection() { ui->requestButton->setEnabled(false); ui->clientStatusLabel->setText(tr("Connecting")); tcpClient.connectToHost(QHostAddress::LocalHost, tcpServer.serverPort()); } // A slot for the newConnection() void Dialog::acceptConnection() { // nextPendingConnection() to accept the pending connection as a connected QTcpSocket. // This function returns a pointer to a QTcpSocket tcpServerConnection = tcpServer.nextPendingConnection(); connect(tcpServerConnection, SIGNAL(readyRead()), this, SLOT(updateServerProgress())); connect(tcpServerConnection, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError))); ui->serverStatusLabel->setText(tr("Accepted connection")); tcpServer.close(); } void Dialog::clientSendMessageToServer() { // called when the TCP client connected to the loopback server bytesToWrite = TotalBytes - (int)tcpClient.write(QByteArray(PayloadSize, '@')); ui->clientStatusLabel->setText(tr("Connected")); } void Dialog::updateServerProgress() { bytesReceived += (int)tcpServerConnection->bytesAvailable(); tcpServerConnection->readAll(); ui->serverProgressBar->setMaximum(TotalBytes); ui->serverProgressBar->setValue(bytesReceived); ui->serverStatusLabel->setText(tr("Received %1MB") .arg(bytesReceived / (1024 * 1024))); if (bytesReceived == TotalBytes) { tcpServerConnection->close(); ui->startButton->setEnabled(true); #ifndef QT_NO_CURSOR QApplication::restoreOverrideCursor(); #endif } } void Dialog::updateClientProgress(qint64 numBytes) { // callen when the TCP client has written some bytes bytesWritten += (int)numBytes; // only write more if not finished and when the Qt write buffer is below a certain size. if (bytesToWrite > 0 && tcpClient.bytesToWrite() <= 4*PayloadSize) bytesToWrite -= (int)tcpClient.write(QByteArray(qMin(bytesToWrite, PayloadSize), '@')); ui->clientProgressBar->setMaximum(TotalBytes); ui->clientProgressBar->setValue(bytesWritten); ui->clientStatusLabel->setText(tr("Sent %1MB") .arg(bytesWritten / (1024 * 1024))); } void Dialog::displayError(QAbstractSocket::SocketError socketError) { if (socketError == QTcpSocket::RemoteHostClosedError) return; QMessageBox::information(this, tr("Network error"), tr("The following error occurred: %1.") .arg(tcpClient.errorString())); tcpClient.close(); // Calling close() makes QTcpServer stop listening for incoming connections. tcpServer.close(); ui->clientProgressBar->reset(); ui->serverProgressBar->reset(); ui->clientStatusLabel->setText(tr("Client ready")); ui->serverStatusLabel->setText(tr("Server ready")); ui->startButton->setEnabled(true); ui->requestButton->setEnabled(false); #ifndef QT_NO_CURSOR QApplication::restoreOverrideCursor(); #endif }
Qt 5 Tutorial
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization