转载请声明原创地址哟。
PS:对这块不熟有错误请指出
这换行有毒,谅解。
概述: C++和HTML通过websocket通信,通过官方的qwebchannel.js实现。 C++和HTML共用一个对象并以此进行通信 C++通知HTML:通过信号 HTML通知C++:直接调用其函数 核心类:QWebChannel QT版本:5.6
首先:例子是使用QT自带的例子 大致组成: WebSocketClientWrapper:一个包装为了将WebSocketTransport作为信号参数让QWebChannel能接受到 WebSocketTransport:关键点之一,继承自QWebChannelAbstractTransport Dialog:用于通信的对象 index.html:演示用的html文件 qwebchannel.js:关键点之一,本人是搜索了整个QT找出来的有3个同名文件大小有些区别,具体内容没看过有哪些区别,看目录也就应该知道选哪个了,没有的话运行会报错的,功能:类似管理吧,即存储着注册对象的方法,信号之类的,当然要处理收到的信息后才能存下来。
代码分析: PS:C++是服务端,HTML是客户端 本人:先把其他的大致看一下然后从main出发 main.cpp
#include "qwebchannel.h"
#include <QApplication>
#include <QDialog>
#include <QVariantMap>
#include <QDesktopServices>
#include <QUrl>
#include <QDir>
#include <QFileInfo>
#include <QtWebSockets/QWebSocketServer>
#include "../shared/websocketclientwrapper.h"
#include "../shared/websockettransport.h"
#include "ui_dialog.h"
class Dialog : public QObject
{
Q_OBJECT
public:
explicit Dialog(QObject *parent = 0)
: QObject(parent)
{
ui.setupUi(&dialog);
dialog.show();
connect(ui.send, SIGNAL(clicked()), SLOT(clicked()));
}
void displayMessage(const QString &message)
{
ui.output->appendPlainText(message);
}
signals:
void sendText(const QString &text);
public slots:
void receiveText(const QString &text)
{
displayMessage(tr("Received message: %1").arg(text));
}
private slots:
void clicked()
{
const QString text = ui.input->text();
if (text.isEmpty()) {
return;
}
emit sendText(text);
displayMessage(tr("Sent message: %1").arg(text));
ui.input->clear();
}
private:
QDialog dialog;
Ui::Dialog ui;
};
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QFileInfo jsFileInfo(QDir::currentPath() + "/qwebchannel.js");
if (!jsFileInfo.exists())
QFile::copy(":/qtwebchannel/qwebchannel.js",jsFileInfo.absoluteFilePath());
//这只是为了能把qwebchannel.js放到指定目录能让index.html找到,在我这然并卵.手动复制
QWebSocketServer server(QStringLiteral("QWebChannel Standalone Example Server"), QWebSocketServer::NonSecureMode);
//创建webSocket服务端,第二个参数应该是和关闭防火墙类似的操作,不进行安全检测
if (!server.listen(QHostAddress::LocalHost, 12345)) {
qFatal("Failed to open web socket server.");
return 1;
}
//创建完后肯定要绑定啦,地址端口号要和HTML中的一样
WebSocketClientWrapper clientWrapper(&server);
/* 为了能将接受到的新连接的套接字包装成WebSocketTransport用信号传递给
** QWebChannel的void QWebChannel::connectTo(QWebChannelAbstractTransport *transport)
** 而接受参数正好是QWebChannelAbstractTransport,WebSocketTransport就是它的子类
*/
QWebChannel channel;
QObject::connect(&clientWrapper, &WebSocketClientWrapper::clientConnected,
&channel, &QWebChannel::connectTo);
/*
** 当有新连接即html端打开,serve将获得与之相连的新socket,接着触发WebSocketClientWrapper槽
** 将socket作为WebSocketTransport参数实例化这个对象,然后作为信号参数传递给QWebChannel
** QWebChannel就获得了与html通话的能力
*/
Dialog dialog;
channel.registerObject(QStringLiteral("dialog"), &dialog);//注册后能在HTML中使用
//感觉就是这了,将Dialog的信号,函数等信息发给qwebchannel让其处理
QUrl url = QUrl::fromLocalFile(BUILD_DIR "/index.html");
QDesktopServices::openUrl(url);
//这两句只是自动打开html而已,可以手动打开的哟
//为了照顾下面那一行代码才没有将其删除
dialog.displayMessage(QObject::tr("Initialization complete, opening browser at %1.").arg(url.toDisplayString()));
return app.exec();
}
#include "main.moc"
/*
* 一般情况下,你的类会单独写,一个 ***.h, 一个 ***.cpp 。 类的定义在 头文件中,qmake可以调用moc自动处理(生成moc_***.h)并自动包含。
但你要是偷懒,比如把main函数和类的定义及实现都放到一个文件内,比如取名 main.cpp ,这时就不那么走运了,moc处理后生成 同名 的 .moc 文件,但这时你必须自己包含它了。
*/
服务端小结: 1.准备serve,绑定地址端口 2.收到链接后交给QWebChannel 3.准备对象用于被注册(感觉随便啦,有信号,有方法就行)PS:应该是C++和HTML共用一个实例 只是C++是直接调用,HTML是通过QWebChannel调用
服务端暂时这样了,接下来看看客户端吧 index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="./qwebchannel.js"></script>
<script type="text/javascript">
//BEGIN SETUP
function output(message)
{
var output = document.getElementById("output");
output.innerHTML = output.innerHTML + message + "\n";
}
//为了在打印信息而准备的方法
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实例,连接到服务器
//几个事件的绑定
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.");
//将socket交给qwebchannel.js,处理完后回调
new QWebChannel(socket, function(channel) {
// make dialog object accessible globally
window.dialog = channel.objects.dialog;
//应该是设置全局对象吧,objects里应该就存放着注册的对象
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);
}
//HTML的send按钮的点击事件处理
dialog.sendText.connect(function(message) {
output("Received message: " + message);
});
//C++调用sendText时会使用到这里的匿名函数
dialog.receiveText("Client connected, ready to send/receive messages!");
//HTML直接调用函数
output("Connected to WebChannel, ready to send/receive messages!");
});
}
}
//END SETUP
</script>
<style type="text/css">
html {
height: 100%;
width: 100%;
}
#input {
width: 400px;
margin: 0 10px 0 0;
}
#send {
width: 90px;
margin: 0;
}
#output {
width: 500px;
height: 300px;
}
</style>
</head>
<body>
<textarea id="output"></textarea><br />
<input id="input" /><input type="submit" id="send" value="Send" onclick="javascript:click();" />
</body>
</html>
客户端小结: 1.链接到服务器,并写好对应事件的绑定 2.new QWebChannel 3.qwebchannel.js会帮我们处理的
最后注意: 这些也是得有的在WebSocketTransport中
不要随便删。JSON也是要的传输的是JSON格式的数据
JS方面的能力比较渣,有错请喷,想深入了解的话可以去看源码,能力不足看晕了,不过这个qwebchannel.js倒是可以看一下,才400来行。 还是不太熟悉直接在参数里写函数,总觉得别扭,怪怪的。
FROM:https://my.oschina.net/LiangJYue/blog/668427