Web Sockets

The WebSockets module provides an implementation of the WebSockets protocol for WebSockets clients and servers. It mirrors the Qt CPP module. It allows sending a string and binary messages using a full duplex communication channel. A WebSocket is normally established by making an HTTP connection to the server and the server then “upgrades” the connection to a WebSocket connection.

In Qt/QML you can also simply use the WebSocket and WebSocketServer objects to creates direct WebSocket connection. The WebSocket protocol uses the “ws” URL schema or “wss” for a secure connection.

You can use the web socket qml module by importing it first.

  1. import QtWebSockets
  2. WebSocket {
  3. id: socket
  4. }

To test your web socket we will use the echo server from http://websocket.orgWeb Sockets - 图1 (opens new window).

  1. import QtQuick
  2. import QtWebSockets
  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: function (message) {
  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. }

You should see the ping message we send socket.sendTextMessage("ping") as response in the text field.

image

WS Server

You can easily create your own WS server using the C++ part of the Qt WebSocket or use a different WS implementation, which I find very interesting. It is interesting because it allows connecting the amazing rendering quality of QML with the great expanding web application servers. In this example, we will use a Node JS based web socket server using the wsWeb Sockets - 图3 (opens new window) module. For this, you first need to install node jsWeb Sockets - 图4 (opens new window). Then, create a ws_server folder and install the ws package using the node package manager (npm).

The code shall create a simple echo server in NodeJS to echo our messages back to our QML client.

image

  1. cd ws_server
  2. npm install ws

The npm tool downloads and installs the ws package and dependencies into your local folder.

A server.js file will be our server implementation. The server code will create a web socket server on port 3000 and listens to an incoming connection. On an incoming connection, it will send out a greeting and waits for client messages. Each message a client sends on a socket will be sent back to the client.

  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);

You need to get used to the notation of JavaScript and the function callbacks.

WS Client

On the client side, we need a list view to display the messages and a TextInput for the user to enter a new chat message.

We will use a label with white color in the example.

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

Our chat view is a list view, where the text is appended to a list model. Each entry is displayed using a row of prefix and message label. We use a cell width cw factor to split the with into 24 columns.

  1. // ChatView.qml
  2. import QtQuick
  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. }

The chat input is just a simple text input wrapped with a colored border.

  1. // ChatInput.qml
  2. import QtQuick
  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. }

When the web socket receives a message it appends the message to the chat view. Same applies for a status change. Also when the user enters a chat message a copy is appended to the chat view on the client side and the message is sent to the server.

  1. // ws_client.qml
  2. import QtQuick
  3. import QtWebSockets
  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: function (text) {
  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: function (message) {
  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. }

You need first run the server and then the client. There is no retry connection mechanism in our simple client.

Running the server

  1. cd ws_server
  2. node server.js

Running the client

  1. cd ws_client
  2. qml ws_client.qml

When entering text and pressing enter you should see something like this.

image