綫程化 Fortune 服務器範例

綫程化 Fortune 服務器範例展示如何創建服務器,為使用綫程處理來自不同客戶端的請求的簡單網絡服務。它旨在與 Fortune 客戶端範例一起運行。

此範例的實現類似於 Fortune 服務器 範例,但這裏我們將實現子類化的 QTcpServer 在不同綫程啓動每個連接。

為此,我們需要 2 個類:FortuneServer QTcpServer 子類,和 FortuneThread 繼承 QThread .

class FortuneServer : public QTcpServer
{
    Q_OBJECT
public:
    FortuneServer(QObject *parent = nullptr);
protected:
    void incomingConnection(qintptr socketDescriptor) override;
private:
    QStringList fortunes;
};
					

FortuneServer 繼承 QTcpServer 並重實現 QTcpServer::incomingConnection ()。我們還使用它存儲隨機 Fortune 列錶。

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 構造函數以簡單生成 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();
}
					

我們實現的 QTcpServer::incomingConnection () 創建 FortuneThread 對象,把傳入套接字描述符和隨機 Fortune 傳遞給 FortuneThread 構造函數。通過把 FortuneThread 的 finished() 信號連接到 QObject::deleteLater (),確保綫程獲得刪除一旦它已完成。然後可以調用 QThread::start () 啓動綫程。

class FortuneThread : public QThread
{
    Q_OBJECT
public:
    FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent);
    void run() override;
signals:
    void error(QTcpSocket::SocketError socketError);
private:
    int socketDescriptor;
    QString text;
};
					

轉到 FortuneThread 類,這是 QThread 子類,其工作是把 Fortune 寫入連接套接字。類重實現 QThread::run (),且它擁有報告錯誤的信號。

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

FortuneThread 構造函數僅僅存儲套接字描述符和 Fortune 文本,以便稍後它們可用於 run()。

void FortuneThread::run()
{
    QTcpSocket tcpSocket;
					

run() 函數要做的第一件事是創建 QTcpSocket 對象在堆棧。值得注意的是,我們在綫程內創建此對象,自動把套接字關聯到綫程的事件循環。這確保 Qt 不會試著從主綫程嚮套接字交付事件,當我們從 FortuneThread::run() 訪問它時。

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

套接字被初始化通過調用 QTcpSocket::setSocketDescriptor (),傳遞套接字描述符作為自變量。期望這能成功,但為確保 (盡管不太可能,係統可能會耗盡資源),我們捕獲返迴值並報告任何錯誤。

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

就像 Fortune 服務器 範例,我們把 Fortune 編碼成 QByteArray 使用 QDataStream .

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

不像先前範例,結束是通過調用 QTcpSocket::waitForDisconnected (),阻塞調用綫程直到套接字已斷開連接。因為在單獨的綫程中運行,GUI 將保留響應速度。

範例工程 @ code.qt.io

另請參閱 Fortune 服務器範例 , Fortune 客戶端範例 ,和 阻塞 Fortune 客戶端範例 .