Boire ou coder ... Pourquoi choisir?
Publié le 12 avril 2010 18:00

Les WebSockets facile avec Cramp

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();
}
Au chargement de la page, nous initialisons l'objet socket que nous créerons ensuite. Cependant, nous ne faisons cela que si le navigateur supporte les WebSockets. Si la méthode window.WebSocket n'est pas présente, on affiche donc un gentil message d'information. Sinon on charge la suite !

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 !');
}
Comme on est super gentil et que c'est juste une application de test, on informe l'utilisateur que son socket a bien été ouvert.

_onclose: function() {
    alert('Socket fermé !');
    this._ws = null;
}
Lorsque le socket est fermé, on libère la mémoire.

_onmessage: function(m) {
    alert('Je viens de recevoir un message : ' + m.data);
}
A chaque fois qu'un message (une nouvelle ligne dans le document) est envoyé, on affiche celui-ci à l'utilisateur.

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
L'appel à ces deux méthodes permet de définir quelles méthodes de notre classe seront appelées lorsque la page sera lancée et lorsque son exécution sera arrêtée.

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
On libère la mémoire du timer.

Enfin dans la méthode check_tweets

def check_tweets
    render self.twitter.status.text
end
On affiche sur la page le status twitter.

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

Bruno Michel
Bruno Michel dit: 12 avril 2010 22:00 Site web

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

Nicolas Mérouze
Nicolas Mérouze dit: 12 avril 2010 22:00 Site web

Faut pas oublier que c'est aussi dans les WebKit nightlies ;)

Postez un commentaire

Markdown activé