Qt5 Tutorial QTcpServer with Multithreading - 2020
In this tutorial, we will learn how to setup Multithreaded Client and Server using QTcpServer.
Note: Qt5 document
The QTcpServer class provides a TCP-based server.
This class makes it possible to accept incoming TCP connections. You can specify the port or have QTcpServer pick one automatically. You can listen on a specific address or on all the machine's addresses.
Call listen() to have the server listen for incoming connections. The newConnection() signal is then emitted each time a client connects to the server.
Call nextPendingConnection() to accept the pending connection as a connected QTcpSocket. The function returns a pointer to a QTcpSocket in QAbstractSocket::ConnectedState that you can use for communicating with the client.
If an error occurs, serverError() returns the type of error, and errorString() can be called to get a human readable description of what happened.
When listening for connections, the address and port on which the server is listening are available as serverAddress() and serverPort().
Calling close() makes QTcpServer stop listening for incoming connections. Although QTcpServer is mostly designed for use with an event loop, it's possible to use it without one. In that case, you must use waitForNewConnection(), which blocks until either a connection is available or a timeout expires.
For TCP Socket, please visit my C++ Tutorials: Socket - Server and Client.
For Multithreading, please visit my C++ Tutorials: Multithreading in C++.
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 = MultiThreadedQTcpServer CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp
Then, we want to create a new class called MyServer.
Another class called MyThread should be created as well.
Let's work on MyThread. First the header.
This is the thread that our connections are going to run in. Also, keep in mind that whenever there is a new connection, a new thread should be created.
As we've done in the previous tutorial for multithreading, following codes needs to be added to mythread.h:
- <QTcpSocket> should be included.
- We need run() method.
- private member:
QTcpSocket *socket;
int socketDescriptor; - public slots:
void readyRead();
void disconnected(); - Add thread ID into the MyThread constructor as the first parameter.
So, the head file should look like this:
#ifndef MYTHREAD_H #define MYTHREAD_H #include <QThread> #include <QTcpSocket> #include <QDebug> class MyThread : public QThread { Q_OBJECT public: explicit MyThread(qintptr ID, QObject *parent = 0); void run(); signals: void error(QTcpSocket::SocketError socketerror); public slots: void readyRead(); void disconnected(); private: QTcpSocket *socket; qintptr socketDescriptor; }; #endif // MYTHREAD_H
Then, the implementation of MyThread:
#include "mythread.h" MyThread::MyThread(qintptr ID, QObject *parent) : QThread(parent) { this->socketDescriptor = ID; } void MyThread::run() { // thread starts here qDebug() << " Thread started"; socket = new QTcpSocket(); // set the ID if(!socket->setSocketDescriptor(this->socketDescriptor)) { // something's wrong, we just emit a signal emit error(socket->error()); return; } // connect socket and signal // note - Qt::DirectConnection is used because it's multithreaded // This makes the slot to be invoked immediately, when the signal is emitted. connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection); connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected())); // We'll have multiple clients, we want to know which is which qDebug() << socketDescriptor << " Client connected"; // make this thread a loop, // thread will stay alive so that signal/slot to function properly // not dropped out in the middle when thread dies exec(); } void MyThread::readyRead() { // get the information QByteArray Data = socket->readAll(); // will write on server side window qDebug() << socketDescriptor << " Data in: " << Data; socket->write(Data); } void MyThread::disconnected() { qDebug() << socketDescriptor << " Disconnected"; socket->deleteLater(); exit(0); }
Note that Qt::DirectConnection is used for the readyRead connection. This is because it's multithreaded, otherwise we may have some operation issues. This makes the slot to be invoked immediately, when the signal is emitted.
enum Qt::ConnectionType:
This enum describes the types of connection that can be used between signals and slots. In particular, it determines whether a particular signal is delivered to a slot immediately or queued for delivery at a later time.
Constant | Value | Description |
---|---|---|
Qt::AutoConnection | 0 | (default) If the signal is emitted from a different thread than the receiving object, the signal is queued, behaving as Qt::QueuedConnection. Otherwise, the slot is invoked directly, behaving as Qt::DirectConnection. The type of connection is determined when the signal is emitted. |
Qt::DirectConnection | 1 | The slot is invoked immediately, when the signal is emitted. |
Qt::QueuedConnection | 2 | The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread. |
Qt::BlockingQueuedConnection | 3 | (Same as QueuedConnection, except the current thread blocks until the slot returns. This connection type should only be used where the emitter and receiver are in different threads. |
Now, it might better check if our code runs OK.
Up until now, we haven't done anything with MyServer class.
Note that the class inherits from QTcpServer:
class MyServer : public QTcpServer
Two major things should be done:
- Start the server.
- Should be able to handle the incoming connections.
Here is the new code (myserver.h):
// myserver.h #ifndef MYSERVER_H #define MYSERVER_H #include <QTcpServer> class MyServer : public QTcpServer { Q_OBJECT public: explicit MyServer(QObject *parent = 0); void startServer(); signals: public slots: protected: void incomingConnection(qintptr socketDescriptor); }; #endif // MYSERVER_H
Now, move on to implementation, myserver.cpp:
// myserver.cpp #include "myserver.h" #include "mythread.h" MyServer::MyServer(QObject *parent) : QTcpServer(parent) { } void MyServer::startServer() { int port = 1234; if(!this->listen(QHostAddress::Any, port)) { qDebug() << "Could not start server"; } else { qDebug() << "Listening to port " << port << "..."; } } // This function is called by QTcpServer when a new connection is available. void MyServer::incomingConnection(qintptr socketDescriptor) { // We have a new connection qDebug() << socketDescriptor << " Connecting..."; // Every new connection will be run in a newly created thread MyThread *thread = new MyThread(socketDescriptor, this); // connect signal/slot // once a thread is not needed, it will be beleted later connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); }
As we discussed earlier, we implemented two things: staring the server and handling the incoming connections.
This virtual function is called by QTcpServer when a new connection is available. The socketDescriptor argument is the native socket descriptor for the accepted connection.
Now, let's make a server and starts it.
#include <QCoreApplication> #include "myserver.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // Make a server and starts it MyServer server; server.startServer(); return a.exec(); }
Run the code:
Then we get the result as in the picture below saying the server is listening:
Then, open up another cmd window, and send a connection request via telnet:
The picture below shows the two responses from two different clients' connection requests:
Here are the codes used in the example:
main.cpp:
// main.cpp #include <QCoreApplication> #include "myserver.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // Make a server and starts it MyServer server; server.startServer(); return a.exec(); }
mythread.h:
// mythread.h #ifndef MYTHREAD_H #define MYTHREAD_H #include <QThread> #include <QTcpSocket> #include <QDebug> class MyThread : public QThread { Q_OBJECT public: explicit MyThread(qintptr ID, QObject *parent = 0); void run(); signals: void error(QTcpSocket::SocketError socketerror); public slots: void readyRead(); void disconnected(); private: QTcpSocket *socket; qintptr socketDescriptor; }; #endif // MYTHREAD_H
mythread.cpp:
// mythread.cpp #include "mythread.h" MyThread::MyThread(qintptr ID, QObject *parent) : QThread(parent) { this->socketDescriptor = ID; } void MyThread::run() { // thread starts here qDebug() << " Thread started"; socket = new QTcpSocket(); // set the ID if(!socket->setSocketDescriptor(this->socketDescriptor)) { // something's wrong, we just emit a signal emit error(socket->error()); return; } // connect socket and signal // note - Qt::DirectConnection is used because it's multithreaded // This makes the slot to be invoked immediately, when the signal is emitted. connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection); connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected())); // We'll have multiple clients, we want to know which is which qDebug() << socketDescriptor << " Client connected"; // make this thread a loop, // thread will stay alive so that signal/slot to function properly // not dropped out in the middle when thread dies exec(); } void MyThread::readyRead() { // get the information QByteArray Data = socket->readAll(); // will write on server side window qDebug() << socketDescriptor << " Data in: " << Data; socket->write(Data); } void MyThread::disconnected() { qDebug() << socketDescriptor << " Disconnected"; socket->deleteLater(); exit(0); }
myserver.h:
// myserver.h #ifndef MYSERVER_H #define MYSERVER_H #include <QTcpServer> #include "mythread.h" class MyServer : public QTcpServer { Q_OBJECT public: explicit MyServer(QObject *parent = 0); void startServer(); signals: public slots: protected: void incomingConnection(qintptr socketDescriptor); }; #endif // MYSERVER_H
myserver.cpp:
// myserver.cpp #include "myserver.h" #include "mythread.h" MyServer::MyServer(QObject *parent) : QTcpServer(parent) { } void MyServer::startServer() { int port = 1234; if(!this->listen(QHostAddress::Any,port)) { qDebug() << "Could not start server"; } else { qDebug() << "Listening to port " << port << "..."; } } void MyServer::incomingConnection(qintptr socketDescriptor) { // We have a new connection qDebug() << socketDescriptor << " Connecting..."; MyThread *thread = new MyThread(socketDescriptor, this); // connect signal/slot // once a thread is not needed, it will be beleted later connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); }
Note:
It may be helpful to look at the Threaded Fortune Server Example and Threaded Fortune Client Example.
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