Les websockets et le service écho
sur pcDuino
Les cartes pcDuino disposent de la librairie Qt au même titre que les PC classiques. Nous allons en profiter pour
mettre en oeuvre des services qui pourront être exploités au travers d'un Intranet, mais aussi éventuellement depuis Internet. Pour
que cela soit simple à pratiquer, nous mettrons en place un service Websocket
avec le protocole adapté. Je rappelle que la
notion de service sous-entend la possibilité de communiquer avec plusieurs clients en même temps.
Le protocole HTTP est un protocole ancien et très fiable. Toutefois, il possède un gros défaut. Son fonctionnement est le suivant : le client se connecte, il soumet une requête, le serveur lui donne sa réponse et dès que la réponse est reçue par le client, la connexion est tout de suite interrompue. Dans ce système là, à aucun moment le serveur peut envoyer une information de lui-même, par exemple pour notifier une alerte à un client particulier. Ainsi, avec ce protocole HTTP, il n'est pas possible à un client de dialoguer avec un autre client.
Avec le protocole WebSocket, il en va tout autrement. Ce protocole utilise les technologies Web à l'image du protocole HTTP
mais grâce à cette notion de socket, il permet en plus d'établir une connexion permanente et bidirectionnelle. Ainsi, quand ils le
veulent, le client et le serveur peuvent envoyer librement des données à l’autre par ce protocole. Il partage le port HTTP
80 Web avec le contenu existant, traversant facilement les différents pare-feu, routeurs, proxy, etc… La
grande nouveauté, c'est que le serveur peut lui-même, quand il le désire, envoyer des données vers le client et n’est pas obligé
d’attendre que ce dernier ne soumette une requête. L'exemple par excellence est l'utilisation du service DropBox
qui utilise
ces compétences là.
Le protocole des WebSockets change la manière dont un serveur web réagit aux requêtes d'un client : au lieu de fermer la connexion, il envoie un message avec le code HTTP 101 et laisse la connexion ouverte. Les deux parties s'attendent ensuite à recevoir des données et à écrire sur le flux. Contrairement à HTTP, le protocole supporte une communication bilatérale complète de telle manière que le client et le serveur peuvent s'échanger des messages en même temps. Grâce à cette technique, il est maintenant possible à un client de communiquer en temps réel avec un autre client par le biais du service WebSocket, et ceci éventuellement depuis Internet.
Afin de bien maîtriser ces technologies, nous commencerons par un service très simple, nommé écho
, qui renvoie tout
simplement le même texte soumis par le client. Toutefois, nous en profitons pour envoyer en même temps que le message reçu, l'adresse
IP du poste client. Nous utiliserons le navigateur pour soumettre nos messages à l'aide d'un site dédié à la communication à l'aide
du protocole websocket
. Plus tard, nous fabriquerons une application fenêtrée qui remplacera ce site particulier. Vous avez
un exemple d'utilisation ci-dessous :
Une fois que le service echo
est lancée dans la carte pcDuino, plusieurs clients peuvent l'utiliser à l'aide
d'un simple navigateur comme cela vous a été montré précédemment.
La mise en oeuvre du service websocket
se fait au travers d'une classe déjà toute faite et qui se nomme QWebSocketServer.
Le service permettra de communiquer avec plusieurs clients en même temps. Une fois que le service est monté, nous pouvons communiquer
avec un des clients au travers du protocole websocket
à l'aide également d'une classe spécialisée qui se nomme QWebSocket.
Chaque objet de cette classe représente un client en particulier.
slotqui prendra en compte cette requête et réagira en conséquence. Pour chaque client connecté, vous devez proposer une gestion événementielle attitrée qui permettra par la suite au service de communiquer avec chacun d'entre eux.Afin de capitaliser toute cette organisation, nous allons mettre en oeuvre la classe Service, qui est une classe assez générale, qui permet de lancer un
service websocketen choisissant le nom et le port, qui enregistre l'ensemble des clients déjà connectés avec leurs identifiants de connexion et qui prend en compte toute l'ossature événementielle quelque soit la gestion que nous en ferons par la suite. Cette classe sera utilisable dans tous les projets qui utiliseront ce type de service. Pour cela, il sera nécessaire de créer une nouvelle classe qui hérite de cette classe Service, et il faudra redéfinir la méthode receptionMessage() qui récupère le message soumis par un des clients le dernier client qui envoie le message est gardé en mémoire à l'aide de l'attribut client afin de proposer un traitement spécifique à ce que vous souhaitez faire. Justement, pour réaliser le service
echo, nous créons une classe Echo qui hérite de Service et qui permet de renvoyer le message soumis par le dernier client, avec l'adresse IP de son poste.
Dans le fichier de projet, il est nécessaire d'intégrer le module websockets afin que cette technologie soit bien pris en compte. Le réseau n'est pas intégré par défaut. Il suffit juste de le spécifier. Un dernier point, lorsque nous lançons le service, ce dernier reste constamment en fonctionnement. Une solution consiste à aller sur la cible et de tuer kill le programme correspondant. Une autre, qui a été mise en oeuvre ici, consiste à envoyer, à l'aide du navigateur, un message avec le mot stop. Automatiquement le programme s'arrête de lui-même.
echo.pro
QT += core websockets
QT -= gui
CONFIG += console c++11
TEMPLATE = app
SOURCES += main.cpp service.cpp echo.cpp
HEADERS += service.h echo.h
TARGET = echo
target.path = /home/ubuntu/Public
INSTALLS += target
service.h
#ifndef SERVICE_H
#define SERVICE_H
#include <QObject>
#include <QWebSocketServer>
#include <QWebSocket>
#include <vector>
using namespace std;
class Service : public QObject
{
Q_OBJECT
public:
explicit Service(QObject *parent, const char *nom, int port);
protected:
vector<QWebSocket *> connectes;
QWebSocket *client;
private:
QWebSocketServer service;
signals:
void arreter();
public slots:
void nouvelleConnexion();
virtual void receptionMessage(const QString &texte);
void deconnexion();
};
#endif // SERVICE_H
service.cpp
#include "service.h"
#include <algorithm>
Service::Service(QObject *parent, const char *nom, int port) : QObject(parent), service(nom, QWebSocketServer::NonSecureMode, this)
{
if (service.listen(QHostAddress::Any, port))
connect(&service, SIGNAL(newConnection()), this, SLOT(nouvelleConnexion()));
else arreter();
}
void Service::nouvelleConnexion()
{
QWebSocket *client = service.nextPendingConnection();
connectes.push_back(client);
client->sendTextMessage("Bienvenue !");
connect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(receptionMessage(QString)));
connect(client, SIGNAL(disconnected()), this, SLOT(deconnexion()));
}
void Service::receptionMessage(const QString &texte)
{
if (texte=="stop") { arreter(); return; }
client = (QWebSocket *) sender();
}
void Service::deconnexion()
{
QWebSocket *client = (QWebSocket *) sender();
client->deleteLater();
connectes.erase(find(connectes.begin(), connectes.end(), client));
disconnect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(receptionMessage(QString)));
disconnect(client, SIGNAL(disconnected()), this, SLOT(deconnexion()));
}
echo.h
#ifndef ECHO_H
#define ECHO_H
#include "service.h"
class Echo : public Service
{
public:
Echo() : Service(nullptr, "chat", 8080) {}
void receptionMessage(const QString &message) override;
};
#endif // ECHO_H
echo.cpp
#include "echo.h"
void Echo::receptionMessage(const QString &message)
{
Service::receptionMessage(message);
QString adresse = client->peerAddress().toString();
client->sendTextMessage(QString("[%1] > %2").arg(adresse).arg(message));
}
main.cpp
#include <QCoreApplication>
#include "echo.h"
int main(int nombre, char *options[])
{
QCoreApplication appli(nombre, options);
Echo websocket;
QObject::connect(&websocket, SIGNAL(arreter()), &appli, SLOT(quit()));
return appli.exec();
}