跟着例子学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. 其他的均比较容易理解,略去;
- 跟着例子学Qt--2.standalone( C++ QWebChannel server and a HTML/JS client)
- 跟着例子学Qt--1.blockingfortuneclient
- QT程序与html交互(二)------QWebChannel类
- 跟着鱼C学HTML学习记录0
- QWebChannel与JS交互
- Web.js MVC between client and server
- Web.js MVC between client and server
- C Socket Programming for Linux with a Server and Client Example Code
- Writing a Simple Service and Client (C++)
- Qt5实现百度离线地图APIV2_0用QWebChannel实现Qt与js交互
- Apache server type standalone and inetd
- 客户端和服务端程序 server.c and client.c
- thrift C++做server, C++,python, java做Client例子
- thrift C++做server, C++,python, java做Client例子
- thrift C++做server, C++,python, java做Client例子
- how to use a SQLite database in a standalone program with an HTML interface and VBScript as the programming language
- [gsoap] SIGPIPE (Broken pipe) in client/standalone server
- svn client and server
- Leetcode-28. Implement strStr()
- 【基于Java+Selenium+Eclipse的UI自动化(1)】-原理和环境搭建
- 504. Base 7
- Redefinition of enumerator ios
- Java.util.Collections 类
- 跟着例子学Qt--2.standalone( C++ QWebChannel server and a HTML/JS client)
- java实现洗牌
- 链表求和
- 常量对象、常量成员函数和常引用
- Computer Vision local feature matching 局部特征匹配
- Struts1.x和2.X的一些区别
- 图论算法及其模板
- Leetcode: Summary Ranges
- VMWare WorkStation如何合理地设置网卡