Give a Push to Your Qt Application with Qt Cloud Services and WebSockets



Similar documents
New Qt APIs for Mobile Development. Mobility I Alex Blasche

Cloud Elements! Events Management BETA! API Version 2.0

Programmers rejoice: QML makes business people understand. Qt Developer Days 2014 Hinrich Specht 2. September 2014 Folie 1

Surface and Volumetric Data Rendering and Visualisation

[PRAKTISCHE ASPEKTE DER INFORMATIK SS 2014]

ICON UK 2015 node.js for Domino developers. Presenter: Matt White Company: LDC Via

PaaS - Platform as a Service Google App Engine

HTML5 Websockets with ruby and rails. Saurabh Bhatia, CEO, Safew Labs

Repeater. BrowserStack Local. browserstack.com 1. BrowserStack Local makes a REST call using the user s access key to browserstack.

Cloud Powered Mobile Apps with Azure

latest Release 0.2.6

Storage Made Easy Enterprise File Share and Sync (EFSS) Cloud Control Gateway Architecture

The Little Real-time Web Development Book

Introduction to Oracle Mobile Application Framework Raghu Srinivasan, Director Development Mobile and Cloud Development Tools Oracle

Fachgebiet Technische Informatik, Joachim Zumbrägel

The full setup includes the server itself, the server control panel, Firebird Database Server, and three sample applications with source code.

Oracle Communications WebRTC Session Controller: Basic Admin. Student Guide

INSTALLING KAAZING WEBSOCKET GATEWAY - HTML5 EDITION ON AN AMAZON EC2 CLOUD SERVER

Integrating Web Messaging into the Enterprise Middleware Layer

FIVE SIGNS YOU NEED HTML5 WEBSOCKETS

OpenShift on you own cloud. Troy Dawson OpenShift Engineer, Red Hat November 1, 2013

Syllabus INFO-GB Design and Development of Web and Mobile Applications (Especially for Start Ups)

NAS 221 Remote Access Using Cloud Connect TM

DEVELOPING NFC APPS for BLACKBERRY

Programming IoT Gateways With macchina.io

CSCI110: Examination information.

FormAPI, AJAX and Node.js

LabVIEW Internet Toolkit User Guide

PROFESSIONAL. Node.js BUILDING JAVASCRIPT-BASED SCALABLE SOFTWARE. Pedro Teixeira WILEY. John Wiley & Sons, Inc.

Livezilla How to Install on Shared Hosting By: Jon Manning

Understanding Evolution's Architecture A Technical Overview

NotePad No More: - A Personal Survey of HTML5 Developer Toolsets. Stewart Christie - Tizen and HTML5 Community Manager.

Login with Amazon. Getting Started Guide for Websites. Version 1.0

Building Java Servlets with Oracle JDeveloper

Building native mobile apps for Digital Factory

Using the Push Notifications Extension Part 1: Certificates and Setup

Syllabus INFO-UB Design and Development of Web and Mobile Applications (Especially for Start Ups)

AppConnect FAQ for MobileIron Technology Partners! AppConnect Overview

socketio Documentation

Manage Workflows. Workflows and Workflow Actions

Web Cloud Architecture

Cloud Tools Reference Guide. Version: GA

Project 2: Web Security Pitfalls

Version Control! Scenarios, Working with Git!

StriderCD Book. Release 1.4. Niall O Higgins

Mobile development with Apache OFBiz. Ean Schuessler, Brainfood

This section contains information intended to help plan for SocialMiner installation and deployment.

Web 2.0 Technology Overview. Lecture 8 GSL Peru 2014

Drupal CMS for marketing sites

Quick Start Guide. Installation and Setup

Qt and Cloud Services. Sami Makkonen Qt R&D Digia

IBM Watson Ecosystem. Getting Started Guide

Drive new Revenue With PaaS/IaaS. Ruslan Synytsky CTO, Jelastic

IUCLID 5 Guidance and Support

EMPLOYEE LOCATION TRACKING SERVICE

Java Development for the Cloud, present and future. Scott Rich Distinguished Engineer, IBM Rational

Apache Jakarta Tomcat

This course provides students with the knowledge and skills to develop ASP.NET MVC 4 web applications.

Setup Guide Access Manager 3.2 SP3

SECURITY AND REGULATORY COMPLIANCE OVERVIEW

ZeroTurnaround License Server User Manual 1.4.0

Study of HTML5 WebSocket for a Multimedia Communication

Preface. Motivation for this Book

MeshBee Open Source ZigBee RF Module CookBook

Tivoli Endpoint Manager BigFix Dashboard

JOBS PORTAL v1.1. What is Jobs Portal? How does it work? SUMMARY:

Mobile Development with Qt

DocuSign for SharePoint

Cloud Powered Mobile Apps with Microsoft Azure

Avaya Inventory Management System

Stackato PaaS Architecture: How it works and why.

Last time. Today. IaaS Providers. Amazon Web Services, overview

Managing Existing Mobile Apps

All You Can Eat Realtime

New Features of SharePoint 2013

Developing a Web Server Platform with SAPI Support for AJAX RPC using JSON

Reseller Guide. Setting up your Reseller Account in Website Panel

PaaS Operation Manual

Living Requirements Document: Sniffit

FileMaker Server 13. FileMaker Server Help

<Insert Picture Here> Michael Hichwa VP Database Development Tools Stuttgart September 18, 2007 Hamburg September 20, 2007

Office 365: End-User Business Environment Workshop

SwiftScale: Technical Approach Document

Cloud Elements! Marketing Hub Provisioning and Usage Guide!

Collaborative Open Market to Place Objects at your Service

Version of this tutorial: 1.06a (this tutorial will going to evolve with versions of NWNX4)

GeoInt 2015 Watson Workshop

How To Use Netiq Access Manager (Netiq) On A Pc Or Mac Or Macbook Or Macode (For Pc Or Ipad) On Your Computer Or Ipa (For Mac) On An Ip

Samsung KNOX EMM Authentication Services. SDK Quick Start Guide

MarkLogic Server. Node.js Application Developer s Guide. MarkLogic 8 February, Copyright 2016 MarkLogic Corporation. All rights reserved.

ScriptLogic File System Auditor User Guide

Web Developer Toolkit for IBM Digital Experience

Welcome to the Force.com Developer Day

How To Run A Hello World On Android (Jdk) On A Microsoft Ds.Io (Windows) Or Android Or Android On A Pc Or Android 4 (

Architecture and Data Flow Overview. BlackBerry Enterprise Service Version: Quick Reference

Web Server Manual. Mike Burns Greg Pettyjohn Jay McCarthy November 20, 2006

Transcription:

Give a Push to Your Qt Application with Qt Cloud Services and WebSockets

About me

Lauri Nevala nevalau nevalla nevalla Working in Qt Cloud Services team at The Qt Company Over ten years of experience in creating web and mobile based applications

Our Goal Understand how to use Qt Cloud Services in order to send and receive WebSocket messages

What is Qt Cloud Services Enginio Data Storage Storage for Applica/on Data Use with QtEnginio Library Managed Applica3on Run3me Applica/on Pla<orm as a Service Support for Qt/C++, NodeJS, Apache, PHP, MongoDB, MySQL, Redis Managed WebSocket Real- Time Socket Connec/ons with virtually unlimited scalability Use with SDK s and Qt WebSocket Client Library

Enginio Data Storage (EDS) Flexible and powerful cloud data storage with built-in user and data access control

Managed Application Runtime (MAR) Scalable," Multi-language," Multi-database," Application Platform as a Service

Managed Application Runtimes How does it work?

Supported Frameworks Supported frameworks by 3rd party build packs Scala, Clojure, Play, Gradle, Grails, PHP, Go, Meteorite, Perl, Dart, Nginx, Apache, Jekyll

Built-in Services or choose from our cloud based services Enginio Data Storage" Managed WebSocket or choose anything with SDK... Amazon, Azure, Google...

Developer Friendly Deployment Deploy using Git the most common VCS among developers > git push qtc master

Managed WebSocket (MWS) Fully managed service implementing a bi-directional, realtime communication gateway for WebSockets.

Managed WebSocket How does it work?

3. send WebSocket message 4. deliver WebSocket message 1. get WebSocket URI 2. open WebSocket connection { data: '{ "object":{ "id":"541df1c1e5bde554a805b213", "createdat":"2014-09 20T21:29:37.849Z", "device":"fzxc0wg0lvaj", "done":false, "objecttype":"objects.todos", "text":"testi2", "updatedat":"2014-09- 20T21:29:37.849Z", "userid":"user:54196f3f5a3d8b61860381a4", "meta":{ "backendid":"5417e5485a3d8b418a01adb7", "eventname":"create", "requestid":"a671f67b4f41c20b53988a6057b32539", "userid":null ', receivers: { sockets: ['*'], tags: []

WebSockets Consider using WebSockets for instantaneous data without the browser refresh

Where to use? Chats Social feeds Multiplayer games Collaborative editing Sport updates Location based apps Your killer app

Why WebSockets are so great? WebSockets defines an API establishing socket connections between web clients and a server TCP-based protocol Full-duplex, simultaneous bi-directional messaging Uri scheme ws: and wss:

WebSockets and Qt Cloud Services 1. + public socket 2. + + private socket 3. do anything you want

Public sockets https://flic.kr/p/dutbk5

+

WebSocket server Create EDS instance Create MWS instance Create EDS Webhook

WebSocket server Create EDS instance Create MWS instance Create EDS Webhook

WebSocket server Create EDS instance Create MWS instance Create EDS Webhook

Create EDS Webhook http://bit.ly/eds-mws-webhook

EDS MWS App HTTP POST https://mws-eu-1.qtc.io/v1/gateways/mws_gateway_id/webhook_receivers/enginio { data: '{ "object":{ "id":"541df1c1e5bde554a805b213", "createdat":"2014-09 20T21:29:37.849Z", "device":"fzxc0wg0lvaj", "done":false, "objecttype":"objects.todos", "text":"testi2", "updatedat":"2014-09- 20T21:29:37.849Z", "userid":"user:54196f3f5a3d8b61860381a4", "meta":{ "backendid":"5417e5485a3d8b418a01adb7", "eventname":"create", "requestid":"a671f67b4f41c20b53988a6057b32539", "userid":null ', receivers: { sockets: ['*'], tags: [] { send WebSocket message to all sockets "object":{ "id":"541df1c1e5bde554a805b213", "createdat":"2014-09 20T21:29:37.849Z", "device":"fzxc0wg0lvaj", "done":false, "objecttype":"objects.todos", "text":"testi2", "updatedat":"2014-09- 20T21:29:37.849Z", "userid":"user:54196f3f5a3d8b61860381a4", "meta":{ "backendid":"5417e5485a3d8b418a01adb7", "eventname":"create", "requestid":"a671f67b4f41c20b53988a6057b32539", "userid":null

WebSocket server Create a EDS instance Create a MWS instance Create EDS Webhook

Enginio Todo http://bit.ly/qtc-todo

2. create Todo 1. connect to WebSocket 5. refresh UI 3. trigger Webhook 4. send WebSocket message

websocketclient.cpp #include "websocketclient.h" #include <QtCore/QDebug> " QT_USE_NAMESPACE " WebSocketClient::WebSocketClient(const QUrl &url, QObject *parent) : QObject(parent), m_url(url) { connect(&m_websocket, &QWebSocket::connected, this, &WebSocketClient::onConnected); connect(&m_websocket, &QWebSocket::disconnected, this, &WebSocketClient::closed); m_websocket.open(qurl(url)); " void WebSocketClient::onConnected() { qdebug() << "WebSocket connected"; connect(&m_websocket, &QWebSocket::textMessageReceived, this, &WebSocketClient::onTextMessageReceived); " void WebSocketClient::onTextMessageReceived(QString message) { emit onmessagereceived(message);

mainwindow.cpp #define REQUEST_URL https://mws- eu- 1.qtc.io/v1/gateways/MWS_GATEWAY_ID/websocket_uri " MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { m_networkmanager = new QNetworkAccessManager(this); QObject::connect(m_networkManager, &QNetworkAccessManager::finished, this, &MainWindow::requestFinished); getwebsocketurl(); QNetworkReply* MainWindow::getWebSocketUrl() { QNetworkRequest request; request.seturl(qurl(qstring("%1").arg(request_url))); request.setheader(qnetworkrequest::contenttypeheader, "application/json"); " QNetworkReply *reply = m_networkmanager- >get(request); return reply; void MainWindow::requestFinished(QNetworkReply *reply) { QJsonDocument replydata = QJsonDocument::fromJson(reply- >readall()); QJsonObject data = replydata.object(); " m_mwsclient = new WebSocketClient(QUrl(data.value("uri").toString()), this); " QObject::connect(m_mwsClient, &WebSocketClient::onMessageReceived, this, &MainWindow::messageReceived);

void MainWindow::messageReceived(QString message) { qdebug() << "Message received:" << message; QJsonDocument messagejson = QJsonDocument::fromJson(message.toUtf8()); QJsonObject enginioobject = messagejson.object().value("object").toobject(); QJsonObject metaobject = messagejson.object().value("meta").toobject(); QString event = metaobject.value("eventname").tostring(); if(event == "delete" enginioobject.value("device_id").tostring()!= m_device) { qdebug() << "Refresh todos"; QJsonObject query; // reload todos from Enginio m_model- >setquery(query); " query["objecttype"] = QString::fromUtf8("objects.todos"); m_model- >setquery(query); "

Demo

Private sockets https://flic.kr/p/89crwv

+ +

Server-side app // Qt Cloud Services package var qtc = require('qtc'); var qtcconfig = require('../config/qtc- conf'); var mws = new qtc.mws(qtcconfig.mws); module.exports = function(app) { app.get('/api/websocket', function(req, res) { authenticaterequest(req, function(e, session){ if(!e){ mws.createsocket(["user:"+session.userid], function(e, mwsresponse) { res.json(mwsresponse); ); else { res.json(error(403, "Access Denied!")); ); ); " app.post('/api/websocket/messages', function(req, res) { var payload = req.body.payload; console.log('got websocket request:') console.log(payload) mws.send(json.stringify(payload), { sockets: null, tags: [payload.object.userid], function(e, mwsresponse) { console.log('send websocket message:'); console.log(mwsresponse); res.json(mwsresponse); ) );

QML Client http://bit.ly/qtc-mar-todo

2. create Todo 3. insert Todo 1. connect to WebSocket 4. trigger Webhook 6. refresh UI 5. send WebSocket message

main.qml import QtQuick 2.2 import QtQuick.Controls 1.1 import Qt.WebSockets 1.0 import "Storage.js" as Storage " Item { id: app width: 480 height: 800 " WebSocket { id: socket active: true ontextmessagereceived: { console.log(message) Storage.handleWebsocketMessage(message) onstatuschanged: { if (socket.status == WebSocket.Error) { console.log("error: " + socket.errorstring) else if (socket.status == WebSocket.Open) { console.log("open") else if (socket.status == WebSocket.Closed) { console.log("socket closed")

storage.js var REQUEST_URL = "http://qtc- tutorial- todo.qtcloudapp.com" function connecttowebsocket() { var doc = new XMLHttpRequest(); doc.open("get", REQUEST_URL + "/api/websocket", true); doc.setrequestheader('x- todo- session', sessionid); doc.setrequestheader('content- Type', 'application/json'); doc.onreadystatechange = function() { // When ready if (doc.readystate === 4) { // If OK if (doc.status === 200) { var data = JSON.parse(doc.responseText); console.log(doc.responsetext) socket.url = data.uri doc.send()

function handlewebsocketmessage(message) { message = JSON.parse(message) var event = message.meta.eventname var object = message.object if(object.device!== device) { // ignore events created by this app var i if(event === "create") { additem(object.text, object.id) else if(event === "update") { for (i=itemmodel.count; i- - ;) { if( object.id === itemmodel.get(i).itemid) { finishitem(i, false) break; if(event === "delete") { for (i=itemmodel.count; i- - ;) { if( object.id === itemmodel.get(i).itemid) { deleteitem(i, false) break;

Demo Try it yourself! http://bit.ly/qtc-todo-demo username: qtincloud password: qtincloud

Custom WebSocket Server https://flic.kr/p/ct2gqu

em-websocket etc

I want to use!!!1

Using WebSockets with MAR Server must listen to a port that MAR gives to the app To keep idle socket connections alive, the WebSocket server must send a ping control frame to every client at least once in < 60s

Qt WebSocket EchoServer http://bit.ly/qt-echoserver Example can be found from Qt 5.3 examples Bug in Qt 5.3 that prevents deploying it to MAR Fixed in Qt 5.4 Qt 5.4 beta in MAR allows us to test it out

main.cpp #include <QtCore/QCoreApplication> #include "echoserver.h" " int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); int port = 1234; if(qenvironmentvariableisset("port") &&!qenvironmentvariableisempty("port")) { port = qgetenv("port").toint(); EchoServer *server = new EchoServer(port); QObject::connect(server, &EchoServer::closed, &a, &QCoreApplication::quit); " return a.exec();

echoserver.cpp EchoServer::EchoServer(quint16 port, QObject *parent) : QObject(parent), m_pwebsocketserver(new QWebSocketServer(QStringLiteral("Echo Server"), QWebSocketServer::NonSecureMode, this)), m_clients() { if (m_pwebsocketserver- >listen(qhostaddress::any, port)) { QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &EchoServer::pingClients); timer- >start(50000); void EchoServer::pingClients() { if(m_clients.length() > 0) { qdebug() << "Ping clients " << m_clients.length(); for(int i = 0; i< m_clients.length(); i++) { m_clients[i]- >ping(); "

Demo

How to Use With Qt? lanevala@it-l-m0015 ~/Development/qtwebsockets-echoserver-example[master*]$ git push qtc master Counting objects: 1, done. Writing objects: 100% (1/1), 186 bytes 0 bytes/s, done. Total 1 (delta 0), reused 0 (delta 0) -----> Qt app detected Installing Qt 5.4.0-beta-2014-09-25_25 -----> Setting up Qt 5.4.0-beta-2014-09-25_25 -----> Configuring with qmake -----> Compiling with make g++ -c -pipe -O2 -Wall -W -D_REENTRANT -fpie -DQT_NO_DEBUG -DQT_WEBSOCKETS_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB - I.qtcs/Qt/5.4/gcc_64/mkspecs/linux-g++ -I. -I.qtcs/Qt/5.4/gcc_64/include -I.qtcs/Qt/5.4/gcc_64/include/QtWebSockets - I.qtcs/Qt/5.4/gcc_64/include/QtNetwork -I.qtcs/Qt/5.4/gcc_64/include/QtCore -I. -o main.o main.cpp g++ -c -pipe -O2 -Wall -W -D_REENTRANT -fpie -DQT_NO_DEBUG -DQT_WEBSOCKETS_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB - I.qtcs/Qt/5.4/gcc_64/mkspecs/linux-g++ -I. -I.qtcs/Qt/5.4/gcc_64/include -I.qtcs/Qt/5.4/gcc_64/include/QtWebSockets - I.qtcs/Qt/5.4/gcc_64/include/QtNetwork -I.qtcs/Qt/5.4/gcc_64/include/QtCore -I. -o echoserver.o echoserver.cpp /app/.qtcs/qt/5.4/gcc_64/bin/moc -DQT_NO_DEBUG -DQT_WEBSOCKETS_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I/app/.qtcs/Qt/ 5.4/gcc_64/mkspecs/linux-g++ -I/tmp/build -I/app/.qtcs/Qt/5.4/gcc_64/include -I/app/.qtcs/Qt/5.4/gcc_64/include/ QtWebSockets -I/app/.qtcs/Qt/5.4/gcc_64/include/QtNetwork -I/app/.qtcs/Qt/5.4/gcc_64/include/QtCore echoserver.h -o moc_echoserver.cpp g++ -c -pipe -O2 -Wall -W -D_REENTRANT -fpie -DQT_NO_DEBUG -DQT_WEBSOCKETS_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB - I.qtcs/Qt/5.4/gcc_64/mkspecs/linux-g++ -I. -I.qtcs/Qt/5.4/gcc_64/include -I.qtcs/Qt/5.4/gcc_64/include/QtWebSockets - I.qtcs/Qt/5.4/gcc_64/include/QtNetwork -I.qtcs/Qt/5.4/gcc_64/include/QtCore -I. -o moc_echoserver.o moc_echoserver.cpp g++ -Wl,-O1 -Wl,-rpath,/app/.qtcs/Qt/5.4/gcc_64 -Wl,-rpath,/app/.qtcs/Qt/5.4/gcc_64/lib -o echoserver main.o echoserver.o moc_echoserver.o -L/app/.qtcs/Qt/5.4/gcc_64/lib -lqt5websockets -lqt5network -lqt5core -lpthread -----> Discovering process types Procfile declares types -> web -----> Compiled slug size is 108M -----> Deploying app Uploading app container... done. mar-eu-1-twwto7g0 deployed to http://mar-eu-1-twwto8g1.qtcloudapp.com

CONNECTED Does it echo? Does it echo? Echoserver listening on port 5000 Echoserver got new connection Ping clients 1 Echoserver got new pong Echoserver got new message "Does it echo?"

Qt WebChannel The Qt WebChannel module provides a library for seamless integration of C++ and QML applications with HTML/JavaScript clients. Any QObject can be published to remote clients, where its public API becomes available.

Example http://bit.ly/qtc-webchannel Qt WebChannel Standalone example from Qt 5.4 examples Instead of local WebSocket server, we are using WebSocket server running on MAR

WebSocketClientWrapper.cpp /*! Construct the client wrapper with the given url. " All clients connecting to the QWebSocketServer will be automatically wrapped in WebSocketTransport objects. */ WebSocketClientWrapper::WebSocketClientWrapper(const QUrl &url, QObject *parent) : QObject(parent), m_url(url) { qwarning() << "Open WebSocket connection: " << m_url; connect(&m_websocket, &QWebSocket::connected, this, &WebSocketClientWrapper::handleNewConnection); m_websocket.open(url); " /*! Wrap an incoming WebSocket connection in a WebSocketTransport object. */ void WebSocketClientWrapper::handleNewConnection() { qwarning() << "WebSocket connected: " << m_url; emit clientconnected(new WebSocketTransport(&m_webSocket));

main.cpp // wrap WebSocket clients in QWebChannelAbstractTransport objects WebSocketClientWrapper clientwrapper(qurl("wss://mar- eu- 1- twwto7g0.qtcloudapp.com")); " // setup the channel QWebChannel channel; QObject::connect(&clientWrapper, &WebSocketClientWrapper::clientConnected, &channel, &QWebChannel::connectTo); " // setup the dialog and publish it to the QWebChannel Dialog dialog; channel.registerobject(qstringliteral("dialog"), &dialog);

index.html window.onload = function() { var baseurl = "wss://mar- eu- 1- twwto7g0.qtcloudapp.com"; output("connecting to WebSocket server at " + baseurl + "."); var socket = new WebSocket(baseUrl); 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!"); );

Demo

QML HTML Initializing WebChannel... Initialization complete, opening browser at file:/// Users/lanevala/Qt5.4.0/Examples/Qt-5.4/ webchannel/build-standalone- Desktop_Qt_5_4_clang_64bit-Debug/index.html. Received message: Client connected, ready to send/receive messages! Sent message: Test message Connecting to WebSocket server at wss://mar-eu-1- twwto7g0.qtcloudapp.com. WebSocket connected, setting up QWebChannel. Connected to WebChannel, ready to send/receive messages! Received message: Test message Text Message

Summary WebSockets are great Using WebSockets with Qt and Qt Cloud Services is super easy and fast There are multiple levels on you can utilise Qt Cloud Services to send and receive WebSocket messages Use Managed WebSocket service Deploy your own solution to MAR

Further actions Visit qtcloudservices.com Sign-up Use it Give us feedback Qt forum info@qtcloudservices.com

Thank you! Source Codes: Enginio Todo: http://bit.ly/qtc-todo Qt Cloud Services Todo: http://bit.ly/qtc-mar-todo Qt WebSocket Echo Server: http://bit.ly/qt-echoserver