Web Sockets

webSockets不是Qt提供的。将WebSockets加入到Qt/QML中需要花费一些工作。从作者的角度来看WebSockets有巨大的潜力来添加HTTP服务缺少的功能-通知。HTTP给了我们get和post的功能,但是post还不是一个通知。目前客户端轮询服务器来获得应用程序的服务,服务器也需要能通知客户端变化和事件。你可以与QML接口比较:属性,函数,信号。也可以叫做获取/设置/调用和通知。

QML WebSocket插件将会在Qt5中加入。你可以试试来自qt playground的web sockets插件。为了测试,我们使用一个现有的web socket服务实现了echo server。

首先确保你使用的Qt5.2.x。

  1. $ qmake --version
  2. ... Using Qt version 5.2.0 ...

然后你需要克隆web socket的代码库,并且编译它。

  1. $ git clone git@gitorious.org:qtplayground/websockets.git
  2. $ cd websockets
  3. $ qmake
  4. $ make
  5. $ make install

现在你可以在qml模块中使用web socket。

  1. import Qt.WebSockets 1.0
  2. WebSocket {
  3. id: socket
  4. }

测试你的web socket,我们使用来自http://websocket.org的echo server 。

  1. import QtQuick 2.0
  2. import Qt.WebSockets 1.0
  3. Text {
  4. width: 480
  5. height: 48
  6. horizontalAlignment: Text.AlignHCenter
  7. verticalAlignment: Text.AlignVCenter
  8. WebSocket {
  9. id: socket
  10. url: "ws://echo.websocket.org"
  11. active: true
  12. onTextMessageReceived: {
  13. text = message
  14. }
  15. onStatusChanged: {
  16. if (socket.status == WebSocket.Error) {
  17. console.log("Error: " + socket.errorString)
  18. } else if (socket.status == WebSocket.Open) {
  19. socket.sendTextMessage("ping")
  20. } else if (socket.status == WebSocket.Closed) {
  21. text += "\nSocket closed"
  22. }
  23. }
  24. }
  25. }

你可以看到我们使用socket.sendTextMessage(“ping”)作为响应在文本区域中。

Web Sockets - 图1

11.8.1 WS Server

你可以使用Qt WebSocket的C++部分来创建你自己的WS Server或者使用一个不同的WS实现。它非常有趣,是因为它允许连接使用大量扩展的web应用程序服务的高质量渲染的QML。在这个例子中,我们将使用基于web socket的ws模块的Node JS。你首先需要安装node.js。然后创建一个ws_server文件夹,使用node package manager(npm)安装ws包。

  1. $ cd ws_server
  2. $ npm install ws

npm工具下载并安装了ws包到你的本地依赖文件夹中。

一个server.js文件是我们服务器的实现。服务器代码将在端口3000创建一个web socket服务并监听连接。在一个连接加入后,它将会发送一个欢迎并等待客户端信息。每个客户端发送到socket信息都会发送回客户端。

  1. var WebSocketServer = require('ws').Server;
  2. var server = new WebSocketServer({ port : 3000 });
  3. server.on('connection', function(socket) {
  4. console.log('client connected');
  5. socket.on('message', function(msg) {
  6. console.log('Message: %s', msg);
  7. socket.send(msg);
  8. });
  9. socket.send('Welcome to Awesome Chat');
  10. });
  11. console.log('listening on port ' + server.options.port);

你需要获取使用的JavaScript标记和回调函数。

11.8.2 WS Client

在客户端我们需要一个链表视图来显示信息,和一个文本输入来输入新的聊天信息。

在例子中我们使用一个白色的标签。

  1. // Label.qml
  2. import QtQuick 2.0
  3. Text {
  4. color: '#fff'
  5. horizontalAlignment: Text.AlignLeft
  6. verticalAlignment: Text.AlignVCenter
  7. }

我们的聊天视图是一个链表视图,文本被加入到链表模型中。每个条目显示使用行前缀和信息标签。我们使用单元将它分为24列。

  1. // ChatView.qml
  2. import QtQuick 2.0
  3. ListView {
  4. id: root
  5. width: 100
  6. height: 62
  7. model: ListModel {}
  8. function append(prefix, message) {
  9. model.append({prefix: prefix, message: message})
  10. }
  11. delegate: Row {
  12. width: root.width
  13. height: 18
  14. property real cw: width/24
  15. Label {
  16. width: cw*1
  17. height: parent.height
  18. text: model.prefix
  19. }
  20. Label {
  21. width: cw*23
  22. height: parent.height
  23. text: model.message
  24. }
  25. }
  26. }

聊天输入框是一个简单的使用颜色包裹边界的文本输入。

  1. // ChatInput.qml
  2. import QtQuick 2.0
  3. FocusScope {
  4. id: root
  5. width: 240
  6. height: 32
  7. Rectangle {
  8. anchors.fill: parent
  9. color: '#000'
  10. border.color: '#fff'
  11. border.width: 2
  12. }
  13. property alias text: input.text
  14. signal accepted(string text)
  15. TextInput {
  16. id: input
  17. anchors.left: parent.left
  18. anchors.right: parent.right
  19. anchors.verticalCenter: parent.verticalCenter
  20. anchors.leftMargin: 4
  21. anchors.rightMargin: 4
  22. onAccepted: root.accepted(text)
  23. color: '#fff'
  24. focus: true
  25. }
  26. }

当web socket返回一个信息后,它将会把信息添加到聊天视图中。这也同样适用于状态改变。也可以当用户输入一个聊天信息,将聊天信息拷贝添加到客户端的聊天视图中,并将信息发送给服务器。

  1. // ws_client.qml
  2. import QtQuick 2.0
  3. import Qt.WebSockets 1.0
  4. Rectangle {
  5. width: 360
  6. height: 360
  7. color: '#000'
  8. ChatView {
  9. id: box
  10. anchors.left: parent.left
  11. anchors.right: parent.right
  12. anchors.top: parent.top
  13. anchors.bottom: input.top
  14. }
  15. ChatInput {
  16. id: input
  17. anchors.left: parent.left
  18. anchors.right: parent.right
  19. anchors.bottom: parent.bottom
  20. focus: true
  21. onAccepted: {
  22. print('send message: ' + text)
  23. socket.sendTextMessage(text)
  24. box.append('>', text)
  25. text = ''
  26. }
  27. }
  28. WebSocket {
  29. id: socket
  30. url: "ws://localhost:3000"
  31. active: true
  32. onTextMessageReceived: {
  33. box.append('<', message)
  34. }
  35. onStatusChanged: {
  36. if (socket.status == WebSocket.Error) {
  37. box.append('#', 'socket error ' + socket.errorString)
  38. } else if (socket.status == WebSocket.Open) {
  39. box.append('#', 'socket open')
  40. } else if (socket.status == WebSocket.Closed) {
  41. box.append('#', 'socket closed')
  42. }
  43. }
  44. }
  45. }

你首先需要运行服务器,然后是客户端。在我们简单例子中没有客户端重连的机制。

运行服务器

  1. $ cd ws_server
  2. $ node server.js

运行客户端

  1. $ cd ws_client
  2. $ qmlscene ws_client.qml

当输入文本并点击发送后,你可以看到类似下面这样。

Web Sockets - 图2