Les WebSockets sont l'une des nombreuses nouveautés de HTML5. Actuellement supportés par peu de navigateurs, la fonctionnalité qu'ils apportent les rends particulièrement intéressant.
Keep-Alive
Il est possible, en HTTP 1.1, de préciser combien de temps la connexion entre le navigateur et le serveur web restera ouverte. Ainsi, si dans vos entêtes HTTP vous ajoutez les headers suivants :
Kepp-Alive: 300
Connection: keep-alive
La connexion entre votre navigateur et le serveur restera ouverte 300 secondes après la réception du dernier message. A chaque message reçu, le timeout est réinitialisé.
En conséquent, tant que nous sommes connectés, nous pouvons recevoir des messages en temps réel du serveur. A condition de rester sur cette page bien évidemment. En HTML4, ceci est assez peu utile vu que nous ne pouvons utiliser ce genre de connexion en JavaScript.
WebSocket
Avec HTML5 cependant, les WebSocket font leur apparition. Implémentés uniquement dans Chromium pour le moment, ils permettent de se connecter à une page distante, de conserver la connexion et d'exécuter du code à la réception d'un message.
Voici un exemple de WebSocket relativement simple :
window.onload = function() {
if (!window.WebSocket)
alert('Votre navigateur ne supporte pas les web sockets');
else
socket.load();
}
socket = {
_ws: null,
load: function() {
var location = 'ws://localhost/socket'
this._ws = new WebSocket(location);
this._ws.onopen = this._onopen;
this._ws.onmessage = this._onmessage;
this._ws.onclose = this._onclose;
},
_onopen: function() {
alert('Socket ouvert !');
},
_onclose: function() {
alert('Socket fermé !');
this._ws = null;
},
_onmessage: function(m) {
alert('Je viens de recevoir un message : ' + m.data);
}
}
Décortiquons la chose.
window.onload = function() {
if (!window.WebSocket)
alert('Votre navigateur ne supporte pas les web sockets');
else
socket.load();
}
load: function() {
var location = 'ws://localhost/socket'
this._ws = new WebSocket(location);
this._ws.onopen = this._onopen;
this._ws.onmessage = this._onmessage;
this._ws.onclose = this._onclose;
}
Lorsque l'on appelle cette méthode, on initialise le WebSocket. Pour cela, on définit son url (la variable location). Puis on crée l'objet WebSocket et on le stocke dans une variable de classe afin de le récupérer plus tard.
Chaque objet WebSocket possède quelques callbacks qui sont appelés à un moment particulier de la vie de l'objet. onopen est appelé à l'ouverture du socket; onmessage est appelé à chaque fois que l'on a un nouveau message. Et onclose est appelé à la fermeture du socket.
On définit ces méthodes sur des méthodes internes à notre objet.
_onopen: function() {
alert('Socket ouvert !');
}
_onclose: function() {
alert('Socket fermé !');
this._ws = null;
}
_onmessage: function(m) {
alert('Je viens de recevoir un message : ' + m.data);
}
Comme vous pouvez le voir, utiliser les sockets en eux même n'est pas très difficile.
Application en Ruby avec Cramp
Et en Ruby (merci Pratik), il existe déjà un framework compatible rack permettant de créer et maintenir des pages pour websockets : cramp.
J'ai créé un projet nommé twisocket sur github qui reprends les websockets avec Cramp afin d'afficher mon dernier tweet et le remettre à jour automatiquement.
Voyons un peu lib/twisocket/server.rb
require 'cramp/controller'
require 'cramp/model'
require 'twitter'
module Twisocket
class Socket < Cramp::Controller::Websocket
attr_accessor :twitter, :timer
on_start :start_timer
on_finish :end_it
def start_timer
self.twitter = Twitter.user('dmathieu')
#
# We check the tweets once
# And we set a timer to check them every 30 seconds
#
check_tweets
self.timer = EventMachine::PeriodicTimer.new(30) { check_tweets }
end
def check_tweets
render self.twitter.status.text
end
def end_it
self.timer.cancel
end
end
end
Décortiquons à nouveau.
on_start :start_timer
on_finish :end_it
Lorsque la page est lancée, on appelle la méthode start_timer.
def start_timer
self.twitter = Twitter.user('dmathieu')
check_tweets
self.timer = EventMachine::PeriodicTimer.new(30) { check_tweets }
end
Dans cette méthode, on définit l'objet "twitter", qui persistera dans la classe tant que celle-ci existera. Puis on fait un premier appel à la méthode check_tweets. Ensuite on définit un timer avec eventmachine. Notre méthode check_tweets sera appelée toutes les 30 secondes.
Lorsque la page est arrêtée, on exécute la méthode end_it
def end_it
self.timer.cancel
end
Enfin dans la méthode check_tweets
def check_tweets
render self.twitter.status.text
end
Si vous chargez cette page directement dans votre navigateur, vous verrez votre status twitter s'afficher sur une nouvelle ligne toutes les 30 secondes. Cependant si vous reprenez le code précédent, le status twitter sera vous sera transmis en alert() toutes les 30 secondes. Et dans le projet twisocket que j'ai créé, ce même status twitter sera affiché dans la page et remis à jour automatiquement toutes les 30 secondes ...
Plutôt sympa non ?
Et en dehors de Ruby ?
Keep-Alive fait partie du protocole HTTP. Il est donc bien évidemment possible de développer des applications utilisant les WebSockets dans n'importe quel langage De nombreux projets et langages font leur apparition depuis quelques temps afin de faciliter le développement d'applications asynchrones de ce type. Par exemple node qui permet de développer des applications web en javascript (exécuté avec V8) et qui fera peut-être l'objet d'un article dans quelques semaines.
Conclusion
La faible implémentation des WebSockets rends leur utilisation assez difficile pour le moment. Cependant leur potentiel étant particulièrement élevé, tous les navigateurs (sauf un peut-être) devraient les supporter assez rapidement. En attendant il est toujours possible de faire une version alternative en flash si vous êtes des grands fous (n'espérez pas d'article à ce propos en revanche ;) ).


Commentaires
Très intéressant comme article !
Pour info, les websockets, c'est une des priorités pour Firefox, et si tout va bien, ça devrait être disponible dès la prochaine version (3.7).
Faut pas oublier que c'est aussi dans les WebKit nightlies ;)