本页内容

QtRemoteObjects WebSockets 应用程序

在QtRemoteObjects 中使用非QIODevice 传输协议(QWebSocket)。

本示例通过 WebSocket 共享一个QStandardItemModel 。可在wsserver 应用程序的窗口中编辑该模型,更改内容将同步到wsclient 应用程序的窗口中。

这是通过为QWebSocket 实现一个基于QIODevice 的小型封装类WebSocketIoDevice 来实现的。如果 Qt 在编译时启用了 SSL 支持,则会使用 SSL。

包含两栏表格且支持行高亮显示的 SourceView 窗口

WsServer 应用程序

该wsserver 应用程序创建了一个包含两列的QStandardItemModel ,并向其中插入数据。

int main(int argc, char *argv[])
{
    QLoggingCategory::setFilterRules("qt.remoteobjects.debug=false\n"
                                     "qt.remoteobjects.warning=false");
    QApplication app(argc, argv);

    const int modelSize = 100000;
    QStringList list;
    QStandardItemModel sourceModel;
    QStringList hHeaderList;
    hHeaderList << QStringLiteral("First Column with spacing") << QStringLiteral("Second Column with spacing");
    sourceModel.setHorizontalHeaderLabels(hHeaderList);
    list.reserve(modelSize);
    for (int i = 0; i < modelSize; ++i) {
        QStandardItem *firstItem = new QStandardItem(QStringLiteral("FancyTextNumber %1").arg(i));
        if (i == 0)
            firstItem->appendRow(addChild(2, 2));
        QStandardItem *secondItem = new QStandardItem(QStringLiteral("FancyRow2TextNumber %1").arg(i));
        if (i % 2 == 0)
            firstItem->setBackground(Qt::red);
        QList<QStandardItem*> row;
        row << firstItem << secondItem;
        sourceModel.invisibleRootItem()->appendRow(row);
        //sourceModel.appendRow(row);
        list << QStringLiteral("FancyTextNumber %1").arg(i);
    }

随后,它将启动一个绑定到端口 8088 的QWebSocketServer ,并托管该数据模型。

QWebSocketServer webSockServer{QStringLiteral("WS QtRO"), QWebSocketServer::NonSecureMode};
webSockServer.listen(QHostAddress::Any, 8088);

QRemoteObjectHost hostNode;
hostNode.setHostUrl(webSockServer.serverAddress().toString(), QRemoteObjectHost::AllowExternalRegistration);

hostNode.enableRemoting(&sourceModel, QStringLiteral("RemoteModel"), roles);

在处理新连接时,如果 Qt 在编译时已启用 SSL 支持,则会配置 SSL。随后,系统会利用传入的 WebSocketServer 连接创建一个WebSocketIoDevice 。

    QObject::connect(&webSockServer,&QWebSocketServer::newConnection, &hostNode, [&hostNode, &webSockServer]{
        while(autoconn=webSockServer.nextPendingConnection()) {
#ifndef QT_NO_SSL
            // 只要可用,始终使用安全连接
            QSslConfiguration sslConf;
            QFile certFile(QStringLiteral(":/sslcert/server.crt"));
            if(!certFile.open(QIODevice::ReadOnly))
                qFatal("Can't open client.crt file");
            sslConf.setLocalCertificate(QSslCertificate{certFile.readAll()});

            QFile keyFile(QStringLiteral(":/sslcert/server.key"));
            if(!keyFile.open(QIODevice::ReadOnly))
                qFatal("Can't open client.key file");
            sslConf.setPrivateKey(QSslKey{keyFile.readAll(),QSsl::Rsa});

            sslConf.setPeerVerifyMode(QSslSocket::VerifyPeer);
            conn->setSslConfiguration(sslConf);
            QObject::connect(conn,&QWebSocket::sslErrors,conn,&QWebSocket::deleteLater);
#endif
            QObject::connect(conn,&QWebSocket::disconnected,conn,&QWebSocket::deleteLater);
            QObject::connect(conn,&QWebSocket::errorOccurred,conn,&QWebSocket::deleteLater);
            autoioDevice= newWebSocketIoDevice(conn);
            QObject::connect(conn,&QWebSocket::destroyed,ioDevice, &WebSocketIoDevice::deleteLater);
            hostNode.addHostSideConnection(ioDevice);
        }
    });

创建一个 QTreeView,并使用 QStandardItemModel 作为其模型。随后,通过调用 `QTimer::singleShot ` 启动多个定时器,以对模型进行进一步修改。

QTreeView view;
view.setWindowTitle(QStringLiteral("SourceView"));
view.setModel(&sourceModel);
view.show();
TimerHandler handler;
handler.model = &sourceModel;
QTimer::singleShot(5000, &handler, &TimerHandler::changeData);
QTimer::singleShot(10000, &handler, &TimerHandler::insertData);
QTimer::singleShot(11000, &handler, &TimerHandler::changeFlags);
QTimer::singleShot(12000, &handler, &TimerHandler::removeData);
QTimer::singleShot(13000, &handler, &TimerHandler::moveData);

return app.exec();

WsClient 应用程序

wsclient 应用程序会创建一个 QWebSocket,并将该 QWebSocket 作为参数传递给WebSocketIoDevice 。

QScopedPointer<QWebSocket> webSocket{new QWebSocket};
WebSocketIoDevice socket(webSocket.data());

如果 Qt 在编译时启用了对 SSL 的支持,则客户端将配置为使用 SSL。

#ifndef QT_NO_SSL
    // 只要可用,始终使用安全连接
    QSslConfiguration sslConf;
    QFile certFile(QStringLiteral(":/sslcert/client.crt"));
    if(!certFile.open(QIODevice::ReadOnly))
        qFatal("Can't open client.crt file");
    sslConf.setLocalCertificate(QSslCertificate{certFile.readAll()});

    QFile keyFile(QStringLiteral(":/sslcert/client.key"));
    if(!keyFile.open(QIODevice::ReadOnly))
        qFatal("Can't open client.key file");
    sslConf.setPrivateKey(QSslKey{keyFile.readAll(),QSsl::Rsa});

    sslConf.setPeerVerifyMode(QSslSocket::VerifyPeer);
    webSocket->setSslConfiguration(sslConf);
#endif

随后创建一个 `QRemoteObjectNode `,并配置其使用 `WebSocketIoDevice`。接着,它连接到wsserver 。

QRemoteObjectNode node;
node.addClientSideConnection(&socket);
node.setHeartbeatInterval(1000);
webSocket->open(QStringLiteral("ws://localhost:8088"));

创建一个 QTreeView 来显示来自服务器的数据。从节点获取模型,将其设置为 QTreeView 的模型,然后显示该控件。

QTreeView view;
view.setWindowTitle(QStringLiteral("RemoteView"));
view.resize(640,480);
QScopedPointer<QAbstractItemModelReplica> model(node.acquireModel(QStringLiteral("RemoteModel")));
view.setModel(model.data());
view.show();

return app.exec();

示例项目 @ code.qt.io

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