Fortune Server Example#
Demonstrates how to create a server for a network service.
QTcpServer to accept incoming TCP connections, and a simple
QDataStream based data transfer protocol to write a fortune to the connecting client (from the Fortune Client example), before closing the connection.
class Server(QDialog): Q_OBJECT # public Server = explicit(QWidget parent = None) slots: = private() def sendFortune(): # private def initServer(): statusLabel = None() tcpServer = None() fortunes = QList()
The server is implemented using a simple class with only one slot, for handling incoming connections.
tcpServer = QTcpServer(self) if not tcpServer.listen(): QMessageBox.critical(self, tr("Fortune Server"), tr("Unable to start the server: %1.") .arg(tcpServer.errorString())) close() return
In its constructor, our Server object calls
listen() to set up a
QTcpServer to listen on all addresses, on an arbitrary port. In then displays the port
QTcpServer picked in a label, so that user knows which port the fortune client should connect to.
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.")
Our server generates a list of random fortunes that it can send to connecting clients.
When a client connects to our server,
QTcpServer will emit
newConnection() . In turn, this will invoke our sendFortune() slot:
def sendFortune(self): block = QByteArray() out = QDataStream(block, QIODevice.WriteOnly) out.setVersion(QDataStream.Qt_5_10) out << fortunes[QRandomGenerator.global().bounded(fortunes.size())]
The purpose of this slot is to select a random line from our list of fortunes, encode it into a
QDataStream , and then write it to the connecting socket. This is a common way to transfer binary data using
QTcpSocket . First we create a
QByteArray and a
QDataStream object, passing the bytearray to
QDataStream ‘s constructor. We then explicitly set the protocol version of
Qt_4_0 to ensure that we can communicate with clients from future versions of Qt (see
setVersion() ). We continue by streaming in a random fortune.
block = QByteArray() out = QDataStream(block, QIODevice.WriteOnly) out.setVersion(QDataStream.Qt_5_10) out << fortunes[QRandomGenerator.global().bounded(fortunes.size())]
We then call
nextPendingConnection() , which returns the
QTcpSocket representing the server side of the connection. By connecting
deleteLater() , we ensure that the socket will be deleted after disconnecting.
clientConnection.write(block) clientConnection.disconnectFromHost() clientConnection = tcpServer.nextPendingConnection() clientConnection.disconnected.connect( clientConnection.deleteLater)
The encoded fortune is written using
write() , and we finally call
disconnectFromHost() , which will close the connection after
QTcpSocket has finished writing the fortune to the network. Because
QTcpSocket works asynchronously, the data will be written after this function returns, and control goes back to Qt’s event loop. The socket will then close, which in turn will cause
deleteLater() to delete it.