跟着例子学Qt--2.standalone( C++ QWebChannel server and a HTML/JS client)

来源:互联网 发布:什么软件可以借钱 编辑:程序博客网 时间:2024/05/08 10:56

前言

之前使用过QWebChannel来沟通html/js及c++代码,其中很多也不是很了解,当时的使用目的就是将网页内嵌展示,即相当于展示一个本地网页,并在其中暴露一些c++接口,不牵涉到socket的相关通讯。当时理解得很浅,如今恰巧看到Qt有一个关于QWebChannel的例子,代码量不大却几乎完美地阐述了其中的机制,遂细分析之。

概述

其实仔细看分析的Qt standalone示例和我当初使用的方式并不完全相同。standalone中并没有通过QWebEngineView展示网页,这个示例展示的是一个服务器端运用的方式,最后使用QDesktopServices::openUrl打开一个网页而已。

分析主要脉络

    QWebSocketServer server(QStringLiteral("QWebChannel Standalone Example Server"), QWebSocketServer::NonSecureMode);     if (!server.listen(QHostAddress::LocalHost, 12345)) {// 这里让服务器监听端口12345        qFatal("Failed to open web socket server.");        return 1;    }// 后面需要改写这段    WebSocketClientWrapper clientWrapper(&server);    QWebChannel channel;    QObject::connect(&clientWrapper, &WebSocketClientWrapper::clientConnected,                     &channel, &QWebChannel::connectTo);// 后面需要改写这段    Dialog dialog;    channel.registerObject(QStringLiteral("dialog"), &dialog); // 将c++对象暴露到chanel中,于是可以在html/js中访问该c++对象

由于WebSocketClientWrapper 内部还包含了一个自定义的类WebSocketTransport,分析起来比较绕,而WebSocketClientWrapper 干的事情很少,故将例子中部分代码改写下。

QWebChannel channel;    QObject::connect(&server, &QWebSocketServer::newConnection,        &channel, [&channel, &server]() {        channel.connectTo(new WebSocketTransport(server.nextPendingConnection()));    });

这样分析就比较简单了,当有新的连接则channel将通过connectTo连接到我们指定的WebSocketTransport,这里指针并没有显示析构,因为制定了父类为QWebSocket,父类的socket应该会在连接断开后自动析构(个人认为,没有追溯到源码)。
通过上面的简化可知主要精力应该用来分析WebSocketTransport,所幸这些代码也很精炼~

WebSocketTransport::WebSocketTransport(QWebSocket *socket): QWebChannelAbstractTransport(socket), m_socket(socket){    connect(socket, &QWebSocket::textMessageReceived,            this, &WebSocketTransport::textMessageReceived);    connect(socket, &QWebSocket::disconnected,            this, &WebSocketTransport::deleteLater);}/*!    Destroys the WebSocketTransport.*/WebSocketTransport::~WebSocketTransport(){    m_socket->deleteLater(); // WebSocketTransport析构时同时通知下父类进行析构。}/*!    Serialize the JSON message and send it as a text message via the WebSocket to the client.*/void WebSocketTransport::sendMessage(const QJsonObject &message){    // 发送代码    QJsonDocument doc(message);    QString sendStr = QString::fromUtf8(doc.toJson(QJsonDocument::Compact));    m_socket->sendTextMessage(sendStr);}/*!    Deserialize the stringified JSON messageData and emit messageReceived.*/void WebSocketTransport::textMessageReceived(const QString &messageData){    // 接收代码    QJsonParseError error;    QJsonDocument message = QJsonDocument::fromJson(messageData.toUtf8(), &error);    if (error.error) {        qWarning() << "Failed to parse text message as JSON object:" << messageData                   << "Error is:" << error.errorString();        return;    } else if (!message.isObject()) {        qWarning() << "Received JSON message that is not an object: " << messageData;        return;    }    emit messageReceived(message.object(), this);}

代码确实很直白,主要在sendMessage和textMessageReceived里,仅仅做了json与QString的互转。
分别从服务器发送1,2;客户端(浏览器)发送3、4并通过断点输出接收和发送的字串:
sendMessage: {“args”:[“1”],”object”:”dialog”,”signal”:5,”type”:1}
sendMessage: {“args”:[“2”],”object”:”dialog”,”signal”:5,”type”:1}
textMessageReceived: {“type”:6,”object”:”dialog”,”method”:6,”args”:[“3”],”id”:4}
sendMessage: {“data”:null,”id”:4,”type”:10}
textMessageReceived: {“type”:6,”object”:”dialog”,”method”:6,”args”:[“4”],”id”:5}
sendMessage: {“data”:null,”id”:5,”type”:10}
有个疑问,这些代码为什么都是json格式的呢?
查看例子中对应的index.html

window.onload = function() {                if (location.search != "")                    var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]);                else                    var baseUrl = "ws://localhost:12345";                output("Connecting to WebSocket server at " + baseUrl + ".");                var socket = new WebSocket(baseUrl);                socket.onclose = function()                {                    console.error("web channel closed");                };                socket.onerror = function(error)                {                    console.error("web channel error: " + error);                };                socket.onopen = function()                {                    output("WebSocket connected, setting up QWebChannel.");                    new QWebChannel(socket, function(channel) {                        // make dialog object accessible globally                        window.dialog = channel.objects.dialog;                        document.getElementById("send").onclick = function() {                            var input = document.getElementById("input");                            var text = input.value;                            if (!text) {                                return;                            }                            output("Sent message: " + text);                            input.value = "";                            dialog.receiveText(text); // 向服务器发送字符                        }                        dialog.sendText.connect(function(message) { // 服务器注册的对象,在客户端取得并连接信号                            output("Received message: " + message); // 服务器发送字符                        });                        dialog.receiveText("Client connected, ready to send/receive messages!");                        output("Connected to WebChannel, ready to send/receive messages!");                    });                }            }

通过查看html文件可以知道,最后的环境是在html/js中,c++只是向其中注入了一个js对象,所以这里的通讯传输均为json格式,以上WebSocketTransport几乎可以通用。这里由于只有一个对象注入到js环境中,所以每个运行的客户端均共享同一个服务器的dialog对象,这导致服务器发送一个消息后所有客户端均收到相同的消息,这点需要对该对象进行定制适应不同的客户端(//TODO 自己尝试写一下)。

附:分析下html文件

由于对js并不熟悉故很多均来自网络:
1. ws://localhost:12345 ws://和wss://前缀分别表示WebSocket连接和安全的WebSocket连接。参考:http://toplchx.iteye.com/blog/1929427 ;
2. QWebChannel的编写可以参照Qt中的帮助进行编写,可以在Qt帮助中搜索Qt WebChannel找到
JavaScript API查看;还可以在Qt源码中找到qwebchannel.js,这是我环境中qwebchannel.js的目录路径C:\Qt\Qt5.7.1_vs2013\5.7\Src\qtwebchannel\src\webchannel; // TODO 下一章将详细分析下这个文件及其与Qt的对应。
3. 其他的均比较容易理解,略去;

0 0
原创粉丝点击