Threaded Fortune Server Example¶
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.
The implementation of this example is similar to that of the 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
.
class FortuneServer(QTcpServer): Q_OBJECT # public FortuneServer(QObject parent = None) protected: def incomingConnection(socketDescriptor): # private fortunes = QStringList()
FortuneServer inherits QTcpServer
and reimplements incomingConnection()
. We also use it for storing the list of random fortunes.
def __init__(self, parent): QTcpServer.__init__(self, parent) 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.")
We use FortuneServer’s constructor to simply generate the list of fortunes.
def incomingConnection(self, socketDescriptor): fortune = fortunes.at(QRandomGenerator.global().bounded(fortunes.size())) thread = FortuneThread(socketDescriptor, fortune, self) connect(thread, FortuneThread::finished, thread, FortuneThread::deleteLater) thread.start()
Our implementation of 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 deleteLater()
, we ensure that the thread gets deleted once it has finished. We can then call start()
, which starts the thread.
class FortuneThread(QThread): Q_OBJECT # public FortuneThread(int socketDescriptor, QString fortune, QObject parent) def run(): signals: def error(socketError): # private socketDescriptor = int() text = QString()
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 run()
, and it has a signal for reporting errors.
def __init__(self, socketDescriptor, fortune, parent): QThread.__init__(self, parent) self.socketDescriptor = socketDescriptor self.text = fortune
FortuneThread’s constructor simply stores the socket descriptor and fortune text, so that they are available for run() later on.
def run(self): tcpSocket = QTcpSocket()
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().
if (not tcpSocket.setSocketDescriptor(socketDescriptor)) { error.emit(tcpSocket.error()) return
The socket is initialized by calling 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.
block = QByteArray() out = QDataStream(block, QIODevice.WriteOnly) out.setVersion(QDataStream.Qt_4_0) out << text
As with the Fortune Server example, we encode the fortune into a QByteArray
using QDataStream
.
tcpSocket.write(block) tcpSocket.disconnectFromHost() tcpSocket.waitForDisconnected()
But unlike the previous example, we finish off by calling waitForDisconnected()
, which blocks the calling thread until the socket has disconnected. Because we are running in a separate thread, the GUI will remain responsive.
© 2022 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.