Vous souhaitez faire une application web interactive en temps réel ? Rien de mieux que les Websockets !
Quand on parle de websocket, on parle plutôt du protocole Websocket en réalité ! Cette technologie permet d’ouvrir de façon permanente un canal de communication entre un client, votre navigateur, et un serveur reposant sur le protocole TCP qui s’occupe du transport fiable des données.
Le client et le serveur peuvent communiquer entre eux (bidirectionnel) et en même temps (full-duplex).
Le client peut alors envoyer des messages au serveur qui répondra de manière évènementielle, sans avoir reçu de requête du client.
Généralement, on utilise le protocole HTTP pour communiquer entre un client et un serveur. Il est caractérisé par :
Bien que ce protocole permette de faire de nombreuses applications web, il n’est pas optimisé pour les applications dynamiques qui nécessitent des réponses rapides et interactives.
Les websockets ont été créées pour répondre à ces besoins.
On prendra en exemple l’implémentation du protocole Websocket dans une application web Angular (front) / JavaLangage de développement très populaire ! Spring Boot (back).
On verra la connexion Websocket, l’abonnement, l’envoi et la réception de messages et enfin le désabonnement.
Avant tout, il faut ajouter quelques dépendances au pom.xml :
<dependencies>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
On a fait le choix d’utiliser SockJS et STOMP, on reviendra plus en détail sur ces deux librairies.
On crée une classe de configuration qui hérite de WebSocketMessageBrokerConfigurer. Les appels qui utilisent le protocole Websocket seront alors dirigés vers les endpoints (points de connexion) dédiés.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app")
.enableSimpleBroker("/socket");
}
@Override public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/socket")
.setAllowedOrigins("*")
.withSockJS();
}
}
Dans la première méthode, on définit donc deux préfixes pour filtrer les destinations.
Dans la deuxième méthode, on ajoute le point d’entrée pour le handshake : c’est une requête HTTP ayant dans son header, l’option Upgrade qui demande au serveur de se servir dorénavant du protocole Websocket. Le protocole HTTP sera utilisé uniquement ici, pour l’ouverture de la connexion.
On crée un service générique que l’on pourra utiliser partout dans notre application. On retrouve dans les imports les deux librairies ajoutées précédemment : SockJS et STOMP.
import {Injectable} from ’@angular/core’;
import * as Stomp from ’stompjs’;
import * as SockJS from ’sockjs-client’;
import {SERVER_API_URL} from ’../../app.constants’;
@Injectable() export class WebsocketService {
private serverUrl = `${SERVER_API_URL}socket`;
private stompClient;
public mapEndpointSubscription: Map<string, any> = new Map();
public async initWebSocket() {
return new Promise((resolve) => {
if (!this.stompClient) {
const ws = new SockJS(this.serverUrl);
this.stompClient = Stomp.over(ws);
this.stompClient.connect({}, resolve);
} else {
resolve();
}
})
}
}
SockJS fournit un objet Websocket qui fonctionnera sur tous les navigateurs, même ceux qui ne prennent pas en charge le protocole Websocket.
STOMP est un protocole de communication de texte brut avec lequel on pourra se connecter/déconnecter, s’abonner/se désabonner et envoyer des messages.
Dans la méthode initWebsocket(), on crée donc un objet websocket avec l’url pour communiquer avec le serveur et on ouvre une connexion avec la fonction connect(). On dispose ensuite de toutes les méthodes utiles à la communication entre le client et le serveur :
S’abonner : subscribe(name : string, fnc : (event) => void)
public async subscribe(name: string, fnc: (event) => void) {
const subscription = this.stompClient.subscribe(`/${name}`, (event) => {
fnc({...event, body: JSON.parse(event.body)})
});
this.mapEndpointSubscription.set(name, subscription);
}
Se désabonner : unsubscribeToWebSocketEvent(name: string)
public unsubscribeToWebSocketEvent(name: string) {
const subscription = this.mapEndpointSubscription.get(name);
if (subscription) {
subscription.unsubscribe();
}
}
Envoyer un message : send(name: string, body: any)
public send(name: string, body: any) {
this.stompClient.send(`/app/socket/${name}`, {}, JSON.stringify(body));
}
Se déconnecter : disconnect()
public disconnect() {
if (this.stompClient !== null) {
this.stompClient.disconnect();
}
}
Le paramètre « name » correspond au endpoint.
La fonction « fnc » sera exécutée quand quelque chose sera envoyé au endpoint (c’est un callback). Ici, elle permet de récupérer les éléments du message.
Le paramètre « body » correspond au corps du message envoyé.
L’envoi de données, « data transfert », se fait au format texte ou binaire (les données de type texte sont encodées en UTF-8).
Côté back, on peut envoyer un message en ajoutant n’importe où dans notre code Java :
private final MessageSendingOperations<String> wsTemplate;
this.wsTemplate.convertAndSend("/socket/meetingStarts", meeting);
Les clients qui se seront inscrits au endpoint « socket/meetingStarts », recevront l’objet « meeting ».
Côté front, on initialise notre service générique de websockets dans un composant, et on utilise la méthode subscribe(name : string, fnc : (event) => void) pour s’abonner au endpoint « socket/meetingStarts » et récupérer notre objet précédemment envoyé :
private readonly websocketService: WebsocketService;
this.websocketService.initWebSocket().then(() => {
this.websocketService.subscribe(’socket/meetingStarts’, (event) => {
this.currentMeeting = event.body;
});
});
On peut aussi envoyer des messages depuis le front en utilisant la méthode send(name : string, body : any) de notre service :
this.websocketService.send(’someoneJoined’, ’Hello world’);
Les clients qui se seront inscrits au endpoint « socket/someoneJoined », recevront « Hello world ».
Dans ce cas-là, on crée alors un contrôleur côté back qui traitera l’action côté front pour l’envoyer au serveur (on retrouve notre endpoint « socket/someoneJoined ») :
@Controller
public class MeetingController
@MessageMapping("/socket/someoneJoined")
@SendTo("/socket/someoneJoined")
public String someoneJoined(String message) {
return message;
}
}
Quand on n’a plus besoin de recevoir de notifications, on se désabonne du endpoint en utilisant la méthode de notre service :
this.websocketService.unsubscribeToWebSocketEvent(’socket/meetingStarts’);
Le principal point faible dans l’utilisation des websockets, est l’instabilité, si l’application n’est pas single-page. Une application web qui fonctionne avec une page web unique (le contenu, CSSFeuilles de style qui permettent de mettre en forme des pages web., Javascript sont contenus dans un seul fichier HTMLHTML (HyperText Markup Language) est un langage permettant de décrire le découpage d'une page web.) ne se recharge pas entièrement à chaque action demandée par l’utilisateur. Ceci est rendu possible avec AngularAngular est un framework de développement JavaScript populaire basé sur TypeScript., ReactJS ou encore VueJS.
Dans le cas contraire, à chaque changement de page, une nouvelle connexion Websocket est créée, il faut donc bien penser à gérer cela sous forme de sessions.
Le protocole Websocket est plus efficace et performant pour la création d’applications dynamiques, par exemple l’intégration d’un chat au sein d’un site, nécessitera son utilisation. Grâce à la connexion permanente, les requêtes sont plus rapides et plus légères, les utilisateurs recevront donc en temps réel les messages envoyés sous forme de notifications de la part du serveur.
Les Websockets permettent de palier aux besoins auxquels le protocole HTTP ne pouvait répondre ! Et ça, c’est top !
Tuto AXOPEN - On vous explique pas à pas le déploiement de websockets sur un cluster de Node.js
Présentation de la solution de traitement d’image OPENCV et retour sur expérience.
Tomcat 7 offre des solutions de clustering performante et simple d’utilisation. Retour d’expérience sur la mise en place des adresses MULTICAST