Qt5 Tutorial Asynchronous QTcpServer with QThreadPool - 2020
In this tutorial, we will learn how to setup Multithreaded Client and Server using Asynchronous QTcpServer.
QTcpSocket supports two general approaches to network programming:
- 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 connected(). -
The synchronous (blocking) approach.
In non-GUI and multithreaded applications, we can call the waitFor...() functions such as QTcpSocket::waitForConnected() to suspend the calling thread until the operation has completed, instead of connecting to signals.
We'll start with Qt Console Application.
First, we need to add network module to our project file, MultiThreadedQTcpServer.pro:
QT += core QT += network QT -= gui TARGET = QTcpServerThreadPool CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp
Then, we want to create a new class called MyServer.
Another class called MyClient should be created as well.
Another class, MyClient.
The last class, MyTask.
Here is the brief summary. The code will be presented shortly.
- In main(), we create a server and start it:
MyServer server; server.startServer();
- Then, the server starts listening to any incoming connection:
void MyServer::startServer() { if(this->listen(QHostAddress::Any, 1234)) ...
- QTcpServer gets a new connection request:
MyServer::incomingConnection(qintptr socketDescriptor)
- Then, the server makes a new client, and call MyClient::setSocket(qintptr Descriptor):
MyClient *client = new MyClient(this); client->setSocket(socketDescriptor);
- In MyClient::setSocket(qintptr descriptor):
- We make a new QTcpSocket
socket = new QTcpSocket(this);
- Setup signal/slot
- Then initialize the socket with the native socket descriptor:
socket->setSocketDescriptor(descriptor);
- We make a new QTcpSocket
- In slots, especially, massive tasks should be on a thread pool. Otherwise, it will block other socket's task.
In other words, we should throw the task to a new thread but not on current thread (make it asynchronous (non-blocking)). That's exactly what's going on the following line in readyRead():
QThreadPool::globalInstance()->start(mytask);
- The task has been specified in MyTask::run().
- After the task finished, we're going to send the results back to our main thread. In other words, after a task performed a time consuming task, we grab the result in MyClient::TaskResult(int Number):
QByteArray Buffer; Buffer.append("\r\nTask result = "); Buffer.append(QString::number(Number));
and send it to client:socket->write(Buffer);
2. Client is about to request connection using telnet:
3. Client requested a connection and got a response from the server:
4. At a new incoming connection request, the server grabs a thread from the QThreadPool:
Here is the source file: AsyncQTcpServer.zip
main.cpp:
#include <QCoreApplication> #include "myserver.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // Create an instance of a server and then start it. MyServer server; server.startServer(); return a.exec(); }
myserver.h:
// myserver.h #ifndef MYSERVER_H #define MYSERVER_H #include <QTcpServer> #include <QTcpSocket> #include <QAbstractSocket> #include "myclient.h" class MyServer : public QTcpServer { Q_OBJECT public: explicit MyServer(QObject *parent = 0); void startServer(); protected: void incomingConnection(qintptr socketDescriptor); signals: public slots: }; #endif // MYSERVER_H
myserver.cpp:
// myserver.cpp #include "myserver.h" MyServer::MyServer(QObject *parent) : QTcpServer(parent) { } void MyServer::startServer() { if(listen(QHostAddress::Any, 1234)) { qDebug() << "Server: started"; } else { qDebug() << "Server: not started!"; } } void MyServer::incomingConnection(qintptr socketDescriptor) { // At the incoming connection, make a client // and set the socket MyClient *client = new MyClient(this); client->setSocket(socketDescriptor); }
myclient.h:
// myclient.h #ifndef MYCLIENT_H #define MYCLIENT_H #include <QObject> #include <QTcpSocket> #include <QDebug> #include <QThreadPool> #include "mytask.h" class MyClient : public QObject { Q_OBJECT public: explicit MyClient(QObject *parent = 0); void setSocket(qintptr Descriptor); signals: public slots: void connected(); void disconnected(); void readyRead(); // make the server fully ascynchronous // by doing time consuming task void TaskResult(int Number); private: QTcpSocket *socket; }; #endif // MYCLIENT_H
myclient.cpp:
// myclient.cpp #include "myclient.h" MyClient::MyClient(QObject *parent) : QObject(parent) { QThreadPool::globalInstance()->setMaxThreadCount(5); } void MyClient::setSocket(qintptr descriptor) { // make a new socket socket = new QTcpSocket(this); qDebug() << "A new socket created!"; connect(socket, SIGNAL(connected()), this, SLOT(connected())); connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected())); connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead())); socket->setSocketDescriptor(descriptor); qDebug() << " Client connected at " << descriptor; } // asynchronous - runs separately from the thread we created void MyClient::connected() { qDebug() << "Client connected event"; } // asynchronous void MyClient::disconnected() { qDebug() << "Client disconnected"; } // Our main thread of execution // This happening via main thread // Our code running in our current thread not in another QThread // That's why we're getting the thread from the pool void MyClient::readyRead() { qDebug() << "MyClient::readyRead()"; qDebug() << socket->readAll(); // Time consumer MyTask *mytask = new MyTask(); mytask->setAutoDelete(true); // using queued connection connect(mytask, SIGNAL(Result(int)), this, SLOT(TaskResult(int)), Qt::QueuedConnection); qDebug() << "Starting a new task using a thread from the QThreadPool"; // QThreadPool::globalInstance() returns global QThreadPool instance QThreadPool::globalInstance()->start(mytask); } // After a task performed a time consuming task, // we grab the result here, and send it to client void MyClient::TaskResult(int Number) { QByteArray Buffer; Buffer.append("\r\nTask result = "); Buffer.append(QString::number(Number)); socket->write(Buffer); }
mytask.h:
// mytask.h #ifndef MYTASK_H #define MYTASK_H #include <QRunnable> #include <QObject> #include <QRunnable> // Q_OBJECT missing in the original file generated by class wizard. // because we set this class with base class QRunnable // with no inheritance in the class wizard // We do not have this. So, we cannot use signal/slot // But we need them. // Thus, we should use multiple inheritance: QObject inserted here class MyTask : public QObject, public QRunnable { Q_OBJECT public: MyTask(); signals: // notify to the main thread when we're done void Result(int Number); protected: void run(); }; #endif // MYTASK_H
mytask.cpp:
// mytask.cpp #include "mytask.h" #include <QDebug> MyTask::MyTask() { qDebug() << "MyTask()"; } // When the thread pool kicks up // it's going to hit this run, and it's going to do this time consuming task. // After it's done, we're going to send the results back to our main thread. // This runs in the separate thread, and we do not have any control over this thread, // but Qt does. // This may just stay in the queue for several ms depending on how busy the server is. void MyTask::run() { // time consumer qDebug() << "Task started"; int iNumber = 0; for(int i = 0; i < 100; i++) { iNumber += 1; } qDebug() << "Task done"; emit Result(iNumber); }
Qt 5 Tutorial
- 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
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization