1.前言
在现实业务中,经常遇到这样的需求:一端采用web形式开发的,如:客户端采用html、javascript、nodejs开发;而另一端采用C++开发,如:Qt开发的服务端。web页面端需和Qt开发的服务端进行通信、数据交互。即如下图所示模型:
图1:服务端、客户端模式
在这种c/s和b/s混合的模式中,c/s、b/s怎样进行数据交互?它们是怎样进行通信的呢?Qt的QWebChannel、QWebChannelAbstractTransport、QWebSocketServer、QWebSocket这几个类正是为了解决此需求而提出的。
2.QWebSocketServer简介
按照Qt官方助手Qt Assistant对这个类的描述,该类实现一个基于Web的socket套接字服务端。该类模型化了QTcpServer类,其行为和QTcpServer类完全相同,所以,如果知道QTcpServer类用法,就知道了QWebSocketServer用法。该类使得接受一个来自web端的连接变为可能。你可以为该类指定一个端口或让该类自动选择一个端口。你能用该类在某个特定的IP或在整个机器上调用listen()函数去监听即将到来的web端连接。
当每次有新的客户端连接到以本类对象为服务端时,newConnection()信号就会发射。调用nextPendingConnection()函数接受到来的连接,之后该函数返回 一个连接状态的QWebSocket对象。之后,你可以用QWebSocket对象和这个连接的客户端进行通信。
如果某个错误发生,则serverError()返回错误的类型,errorString() 函数返回一个人类可读的描述错误的字符串。当监听连接时,服务端用于监听的IP地址和端口可以通过serverAddress() 、serverPort()函数获取到。调用close()函数使QWebSocketServer停止监听。
3.QWebSocket简介
QWebSocket类实现了同web端进行TCP通信的套接字。该类在一个单一的TCP连接上实现全双工(即:在同一链路上,既能发送数据又能接收数据)的通信机制。 该类模型化了QAbstractSocket类。
4.QWebChannel简介
QWebChannel类向html页面客户端暴露QObject类对象。该类填补了C++应用程序和HTML/JavaScript应用程序之间的技术空白。通过将一个从QObject类派生出的对象发布到QWebChannel类对象上,且在HTML端用 qwebchannel.js ,就可以透明地访问QObject类对象的属性和其pulic类型槽函数和方法,而不需人工传递消息或序列化数据。C++端属性的更新和信号的发射会自动地传送到潜在的、正在运行的远程HTML客户端。在HTML客户端,将会为发布的C++的QObject对象创建一个JavaScript对象。该JavaScript对象镜像、映射了C++端的QObject对象的API,因此调用这些API就相当于调用了C++端的QObject对象的API。
基于C++ 的QWebChannel 类的API,使得同任何HTML表示的客户端进行通信和数据交互变为可能,且能运行在本地或远程机器。唯一的限制是:HTML客户端必须支持qwebchannel.js文件中涉及的JavaScript的特性,例如:应能同任何现代的、支持html的浏览器进行交互或者支持单独的JavaScript运行时库,如:node.js.
5.QWebChannelAbstractTransport
该类是C++ 端QWebChannel 类和HTML/JS表示的客户端之间的通信通道即链路。该类是一个抽象类,必须被子类化后才能使用。QWebChannel 类的使用者必须实现这个类相关接口,且要为每个HTML/JS客户端实现一个QWebChannelAbstractTransport类的实例且连接到QWebChannel 类表示的服务端。
6.这几个类之间的关系
这几个类在图1表示的通信模型、数据交互中各司其责。如前文所示,总结各类职责如下:
QWebSocketServer构建基于TCP通信的一个服务端套接字,等待HTML/JS客户端连接。
当在a步骤提到的HTML/JS客户端连接成功时,就会生成一个QWebSocket类对象。
当a、b步骤都成功时,QWebSocketServer表示的服务端就可以和QWebSocket表示HTML/JS客户端进行数据通信、数据交互。但这样数据通信、数据交互不是很方便,其不方便表现在:通信链路和交互的数据无法用类表示,使用时显得很零散,无法组织数据。QWebChannelAbstractTransport、QWebChannel正是为解决此问题而提出的,前者封装通信链路;后者封装C++、Qt实现的服务器端的属性和接口,之后HTML/JS客户端就可以通过QWebChannelAbstractTransport实现的通信链路调用服务器端属性和接口。
一般地,对于图1所示客户端和服务端交互的时序图类似如下:
图2:时序图
7.工程实战
下面的XX.XX.XX为Qt的版本号,如:5.14.1。下面总结的都是以Qt的5.14.1版本来说明的,未来的版本也许和这有些不同。在Qt安装目录下的
Examples\Qt-XX.XX.XX\webchannel\chatserver
的工程,展示如何利用QWebSocketServer构建一个类似Web服务器,再利用QWebChannel向外部html和javascript网页客户端暴露一个QObject的对象,然后利用从派生的QWebChannelAbstractTransport子类在html和javascript网页与QWebChannel暴露的QObject的对象之间构建通信链路,以使这两者之间可以进行通信和数据交互。
本例子运行步骤如下:
启动chatserver程序,出现控制台界面。
如下:
看看目录Examples\Qt-XX.XX.XX\webchannel\chatclient-html目录下是否有qwebchannel.js文件。如果没有,则进行第3步;如果有,则进行第4步。
将Examples\Qt-XX.XX.XX\webchannel\shared\qwebchannel.js拷贝一份到 Examples\Qt-XX.XX.XX\webchannel\chatclient-html目录。
用浏览器打开Examples\Qt-XX.XX.XX\webchannel\chatclient-html\chatclient.html。在弹出的页面登录框随意输入一个字符串。如下:
登录之后,界面如下:
读者只要弄懂前文提到的几个核心类,理解chatserver工程是很简单的,故本博文不再详细解释chatserver工程,另外在Qt安装目录下的
Examples\Qt-XX.XX.XX\webchannel\standalone
工程,也是利用前文提到的几个核心类实现的,读者可以自行研究。