Threaded Fortune Server

Das Beispiel "Threaded Fortune Server" zeigt, wie man einen Server für einen einfachen Netzwerkdienst erstellt, der Threads verwendet, um Anfragen von verschiedenen Clients zu bearbeiten. Es soll zusammen mit dem Fortune-Client-Beispiel ausgeführt werden.

Die Implementierung dieses Beispiels ist ähnlich wie die des Fortune Server-Beispiels, aber hier wird eine Unterklasse von QTcpServer implementiert, die jede Verbindung in einem anderen Thread startet.

Hierfür benötigen wir zwei Klassen: FortuneServer, eine Unterklasse von QTcpServer, und FortuneThread, die von QThread erbt.

class FortuneServer : public QTcpServer
{
    Q_OBJECT

public:
    FortuneServer(QObject *parent = nullptr);

protected:
    void incomingConnection(qintptr socketDescriptor) override;

private:
    QStringList fortunes;
};

FortuneServer erbt von QTcpServer und implementiert QTcpServer::incomingConnection() neu. Wir verwenden ihn auch zum Speichern der Liste der zufälligen Glücksfälle.

FortuneServer::FortuneServer(QObject *parent)
    : QTcpServer(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.");
}

Wir verwenden den Konstruktor von FortuneServer, um einfach die Liste der Glücksfälle zu erzeugen.

void FortuneServer::incomingConnection(qintptr socketDescriptor)
{
    QString fortune = fortunes.at(QRandomGenerator::global()->bounded(fortunes.size()));
    FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this);
    connect(thread, &FortuneThread::finished, thread, &FortuneThread::deleteLater);
    thread->start();
}

Unsere Implementierung von QTcpServer::incomingConnection() erstellt ein FortuneThread-Objekt und übergibt den eingehenden Socket-Deskriptor und ein zufälliges Vermögen an den FortuneThread-Konstruktor. Indem wir das Signal finished() von FortuneThread mit QObject::deleteLater() verbinden, stellen wir sicher, dass der Thread gelöscht wird, sobald er beendet ist. Anschließend können wir QThread::start() aufrufen, wodurch der Thread gestartet wird.

class FortuneThread : public QThread
{
    Q_OBJECT

public:
    FortuneThread(qintptr socketDescriptor, const QString &fortune, QObject *parent);

    void run() override;

signals:
    void error(QTcpSocket::SocketError socketError);

private:
    qintptr socketDescriptor;
    QString text;
};

Bei der Klasse FortuneThread handelt es sich um eine Unterklasse von QThread, deren Aufgabe es ist, das Fortune in den angeschlossenen Socket zu schreiben. Die Klasse implementiert QThread::run() neu und verfügt über ein Signal zur Fehlermeldung.

FortuneThread::FortuneThread(qintptr socketDescriptor, const QString &fortune, QObject *parent)
    : QThread(parent), socketDescriptor(socketDescriptor), text(fortune)
{
}

Der Konstruktor von FortuneThread speichert einfach den Socket-Deskriptor und den Fortune-Text, so dass sie später für run() verfügbar sind.

void FortuneThread::run()
{
    QTcpSocket tcpSocket;

Das erste, was unsere run()-Funktion tut, ist, ein QTcpSocket -Objekt auf dem Stack zu erzeugen. Bemerkenswert ist, dass wir dieses Objekt innerhalb des Threads erzeugen, wodurch der Socket automatisch mit der Ereignisschleife des Threads verknüpft wird. Dies stellt sicher, dass Qt nicht versucht, Ereignisse vom Haupt-Thread an unseren Socket zu liefern, während wir von FortuneThread::run() aus darauf zugreifen.

    if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
        emit error(tcpSocket.error());
        return;
    }

Der Socket wird durch den Aufruf QTcpSocket::setSocketDescriptor() initialisiert, wobei unser Socket-Deskriptor als Argument übergeben wird. Wir gehen davon aus, dass dies gelingt, aber um sicherzugehen (obwohl es unwahrscheinlich ist, dass dem System die Ressourcen ausgehen), fangen wir den Rückgabewert ab und melden jeden Fehler.

    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_6_5);
    out << text;

Wie beim Fortune-Server-Beispiel kodieren wir das Vermögen in eine QByteArray mit QDataStream.

    tcpSocket.write(block);
    tcpSocket.disconnectFromHost();
    tcpSocket.waitForDisconnected();
}

Im Gegensatz zum vorherigen Beispiel rufen wir zum Abschluss QTcpSocket::waitForDisconnected() auf, was den aufrufenden Thread blockiert, bis die Verbindung zum Socket getrennt wurde. Da wir in einem separaten Thread laufen, bleibt die GUI ansprechbar.

Beispielprojekt @ code.qt.io

Siehe auch Fortune Server, Fortune Client und Blocking Fortune Client.

© 2025 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.