綫程化 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 將保留響應速度。
另請參閱 Fortune 服務器範例 , Fortune 客戶端範例 ,和 阻塞 Fortune 客戶端範例 .