Fortune 服务器

演示如何为网络服务创建服务器。

本示例旨在与“Fortune 客户端”示例或“阻塞式 Fortune 客户端”示例配合运行。

Fortune 服务器示例的屏幕截图

它使用QTcpServer 来接受传入的TCP连接,并采用一种基于QDataStream 的简单数据传输协议,在关闭连接之前将一条“Fortune”信息写入连接的客户端(来自“Fortune Client”示例)。

class Server : public QDialog
{
    Q_OBJECT

public:
    explicit Server(QWidget *parent = nullptr);

private slots:
    void sendFortune();

private:
    void initServer();

    QLabel *statusLabel = nullptr;
    QTcpServer *tcpServer = nullptr;
    QList<QString> fortunes;
};

该服务器通过一个仅含一个槽(用于处理传入连接)的简单类来实现。

    tcpServer = new QTcpServer(this);
    if (!tcpServer->listen()) {
        QMessageBox::critical(this, tr("Fortune Server"),
                              tr("Unable to start the server: %1.")
                              .arg(tcpServer->errorString()));
        close();
        return;
    }
    QString ipAddress;
    const QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
    // use the first non-localhost IPv4 address
    for (const QHostAddress &entry : ipAddressesList) {
        if (entry != QHostAddress::LocalHost && entry.toIPv4Address()) {
            ipAddress = entry.toString();
            break;
        }
    }
    // if we did not find one, use IPv4 localhost
    if (ipAddress.isEmpty())
        ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
    statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n"
                            "Run the Fortune Client example now.")
                         .arg(ipAddress).arg(tcpServer->serverPort()));

在构造函数中,我们的 Server 对象调用QTcpServer::listen() 来设置一个QTcpServer ,以在任意端口上监听所有地址。然后,它在标签中显示选定的端口QTcpServer ,以便用户知道 fortune 客户端应连接到哪个端口。

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

我们的服务器会生成一个随机箴言列表,以便发送给连接的客户端。

connect(tcpServer, &QTcpServer::newConnection, this, &Server::sendFortune);

当客户端连接到我们的服务器时,QTcpServer 将触发QTcpServer::newConnection()。进而,这将调用我们的sendFortune()槽:

void Server::sendFortune()
{
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_6_5);

    out << fortunes[QRandomGenerator::global()->bounded(fortunes.size())];

该插槽的目的是从 fortune 列表中随机选取一行,使用QDataStream 将其编码为QByteArray ,然后写入连接的套接字。这是使用QTcpSocket 传输二进制数据的常见方式。首先,我们创建一个QByteArray 和一个QDataStream 对象,并将字节数组传递给QDataStream 的构造函数。 接着,我们将QDataStream 的协议版本显式设置为QDataStream::Qt_5_10 ,以确保能够与未来版本的Qt客户端进行通信(参见QDataStream::setVersion())。随后,我们将一条随机的“ fortune”信息以流的形式写入。

    QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
    connect(clientConnection, &QAbstractSocket::disconnected,
            clientConnection, &QObject::deleteLater);

接着调用QTcpServer::nextPendingConnection(),该函数返回表示连接服务器端的QTcpSocket 。通过将QTcpSocket::disconnected()与QObject::deleteLater()关联,我们确保断开连接后套接字将被删除。

    clientConnection->write(block);
    clientConnection->disconnectFromHost();
}

使用QTcpSocket::write() 将编码后的箴言写入网络,最后调用QTcpSocket::disconnectFromHost()——当QTcpSocket 完成将箴言写入网络后,该函数将关闭连接。由于QTcpSocket 是异步运行的,数据将在该函数返回后写入,控制权随后将返回给 Qt 的事件循环。 随后套接字将关闭,这将导致QObject::deleteLater() 将其删除。

示例项目 @ code.qt.io

另请参阅 Fortune 客户端和多线程 Fortune 服务器。

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