Serveur Threaded Fortune

L'exemple « Serveur Fortune multithread » montre comment créer un serveur pour un service réseau simple qui utilise des threads pour traiter les requêtes provenant de différents clients. Il est destiné à être exécuté en parallèle de l'exemple « Client Fortune ».

Cette application crée un serveur capable de créer un thread distinct pour chaque client.

L'implémentation de cet exemple est similaire à celle de l'exemple « Fortune Server », mais ici, nous allons implémenter une sous-classe de ` QTcpServer ` qui lance chaque connexion dans un thread différent.

Pour cela, nous avons besoin de deux classes : `FortuneServer`, une sous-classe de ` QTcpServer `, et `FortuneThread`, qui hérite de ` QThread`.

class FortuneServer : public QTcpServer
{
    Q_OBJECT

public:
    FortuneServer(QObject *parent = nullptr);

protected:
    void incomingConnection(qintptr socketDescriptor) override;

private:
    QStringList fortunes;
};

FortuneServer hérite d’ QTcpServer et réimplémente la méthode QTcpServer::incomingConnection(). Nous l’utilisons également pour stocker la liste des prédictions aléatoires.

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.");
}

Nous utilisons le constructeur de FortuneServer pour générer simplement la liste des messages de fortune.

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();
}

Notre implémentation de ` QTcpServer::incomingConnection()` crée un objet `FortuneThread`, en transmettant le descripteur de socket entrant et une prédiction aléatoire au constructeur de `FortuneThread`. En reliant le signal `finished()` de `FortuneThread` à ` QObject::deleteLater()`, nous nous assurons que le thread est supprimé une fois qu’il a terminé. Nous pouvons ensuite appeler ` QThread::start()`, qui lance le thread.

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;
};

Passons maintenant à la classe FortuneThread : il s’agit d’une sous-classe de ` QThread ` dont le rôle est d’écrire la citation sur le socket connecté. La classe réimplémente la méthode ` QThread::run()` et dispose d’un signal permettant de signaler les erreurs.

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

Le constructeur de FortuneThread se contente de stocker le descripteur de socket et le texte de la citation, afin qu’ils soient disponibles ultérieurement pour la fonction `run()`.

void FortuneThread::run()
{
    QTcpSocket tcpSocket;

La première chose que fait notre fonction `run()` est de créer un objet ` QTcpSocket ` sur la pile. Il est important de noter que nous créons cet objet à l’intérieur du thread, ce qui associe automatiquement le socket à la boucle d’événements du thread. Cela garantit que Qt ne tentera pas de transmettre des événements à notre socket depuis le thread principal pendant que nous y accédons depuis FortuneThread::run().

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

La socket est initialisée en appelant QTcpSocket::setSocketDescriptor(), en passant notre descripteur de socket en argument. Nous nous attendons à ce que cela réussisse, mais par mesure de sécurité (bien que cela soit peu probable, le système pourrait manquer de ressources), nous interceptons la valeur de retour et signalons toute erreur.

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

Comme dans l’exemple du serveur Fortune, nous encodons la prédiction dans un objet ` QByteArray ` à l’aide de ` QDataStream`.

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

Mais contrairement à l’exemple précédent, nous terminons en appelant ` QTcpSocket::waitForDisconnected()` (), ce qui bloque le thread appelant jusqu’à ce que la connexion soit interrompue. Comme nous exécutons cette opération dans un thread distinct, l’interface graphique restera réactive.

Exemple de projet sur code.qt.io

Voir également Serveur Fortune, Client Fortune et Client Fortune bloquant.

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