线程化 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 客户端范例 .