スレッド・フォーチュン・サーバー

Threaded Fortune Serverサンプルは、スレッドを使用して異なるクライアントからの要求を処理する、単純なネットワークサービスのサーバーを作成する方法を示しています。これは、Fortune Clientの例と一緒に実行することを目的としています。

この例の実装は、Fortune Serverの例と似ていますが、ここでは、各接続を異なるスレッドで開始するQTcpServer のサブクラスを実装します。

そのためには2つのクラスが必要です:QTcpServer のサブクラスである FortuneServer と、QThread を継承する FortuneThread である。

class FortuneServer : public QTcpServer
{
    Q_OBJECT

public:
    FortuneServer(QObject *parent = nullptr);

protected:
    void incomingConnection(qintptr socketDescriptor) override;

private:
    QStringList fortunes;
};

FortuneServerはQTcpServer を継承し、QTcpServer::incomingConnection ()を再実装している。FortuneServerは、ランダムな運勢のリストを保存するためにも使用する。

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

FortuneServerのコンストラクタを使用して、単純に運勢のリストを生成する。

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

QTcpServer::incomingConnection() の実装では、FortuneThread オブジェクトを作成し、FortuneThread のコンストラクタに受信ソケット記述子とランダムな運勢を渡す。FortuneThread の finished() シグナルをQObject::deleteLater() に接続することで、スレッドが終了したら確実に削除されるようにしている。そして、スレッドを開始するQThread::start ()を呼び出すことができる。

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

FortuneThread クラスに移るが、これはQThread のサブクラスで、接続されたソケットにフォーチュンを書き込むのが仕事である。このクラスはQThread::run() を再インプリメントしており、エラーを報告するためのシグナルを持っている。

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

FortuneThread のコンストラクタは、ソケットディスクリプタとフォーチュンテキストを保存するだけで、後で run() で利用できるようになる。

void FortuneThread::run()
{
    QTcpSocket tcpSocket;

run() 関数が最初に行うことは、スタック上にQTcpSocket オブジェクトを作成することである。注目すべき点は、スレッド内でこのオブジェクトを作成していることです。スレッドのイベントループにソケットを自動的に関連付けます。これにより、FortuneThread::run()からソケットにアクセスしている間に、Qtがメインスレッドからソケットにイベントを送ろうとすることがなくなります。

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

ソケットの初期化はQTcpSocket::setSocketDescriptor() を呼び、引数にソケットディスクリプタを渡す。これが成功することを期待するが、念のため(可能性は低いが、システムがリソースを使い果たす可能性がある)、戻り値をキャッチし、エラーがあれば報告する。

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

フォーチュン・サーバーの例と同様に、QDataStream を使ってフォーチュンをQByteArray にエンコードする。

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

しかし、前の例とは異なり、ソケットが切断されるまで呼び出し元のスレッドをブロックするQTcpSocket::waitForDisconnected()を呼び出して終了する。別スレッドで実行しているので、GUIは応答し続ける。

プロジェクト例 @ code.qt.io

Fortune ServerFortune ClientBlocking Fortune Clientも参照してください

©2024 The Qt Company Ltd. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundationによって発行されたGNU Free Documentation License version 1.3の条項に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。