Jetty & HTML 5 : les WebSockets sont là !

HTML 5 arrive peu à peu dans nos navigateurs et cherche à standardiser certains aspects du Web : vidéo, offline… et push serveur. Autrement formulé, la possibilité de faire des communications bi-directionnelles entre un navigateur et un serveur.

Il existe plusieurs manières de monter une architecture push mais HTML 5 définit les WebSocket dont le principe est simple : le navigateur maintient un lien permanent avec le serveur (grâce à une WebSocket). On parle alors de long polling request (Comet).

Les WebSocket standardisent les API JavaScript mises à disposition par le navigateur pour rendre disponible le push. Ainsi HTML 5 définit un objet de type WebSocket sur lequel on définit trois callbacks que sont, bien évidemment, onopen, onmessage, onclose.

La boîte à outil du push

Côté client, on s’appuiera sur des implémentations d’HTML 5 des navigateurs comme Google Chrome.

Côté serveur, la dernière version de Jetty (7.0.1.v20091125) propose une implémentation des servlets capable de gérer les WebSocket mais on peut noter que la norme Servlet 3 amènera son lot d’évolutions sur cet aspect en incluant un mécanisme d’exécution asynchrone (défini par annotation sur la classe d’implémentation de la servlet)

Voici un exemple (avec Jetty) pour s’en convaincre: une interface de chat permettant à plusieurs utilisateurs d’envoyer des messages aux autres participants.

Configuration serveur

Si vous utilisez Maven, un module dédié est à intégrer pour bénéficier des WebSocket:

org.eclipse.jetty
			jetty-websocket
			${jetty.version}

Le code serveur se représente sous la forme de servlets. La déclaration se fait dans le descripteur de déploiement (web.xml) de votre webapp.

Notre servlet de chat doit hériter d’une classe WebSocketServlet fournie par Jetty. Cette classe diffère d’une servlet « classique » par la présence d’une méthode invoquée à l’ouverture de la connexion:

protected WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)

Cette méthode permet de créer l’objet WebSocket qui sera le pivot d’échange entre le code JavaScript et le code serveur. Dans le cas du chat, le message reçu par un des membres connecté devra être envoyé à l’ensemble des utilisateurs du chat:

protected WebSocket doWebSocketConnect(HttpServletRequest request,
			String protocol) {
		return new ChatWebSocket();
	}
	class ChatWebSocket implements WebSocket {
		private Outbound channel;

		public void onConnect(Outbound outbound) {
			channel = outbound;
			membersWS.add(this);
			LOG.info("New chat client connected");
		}
		public void onMessage(byte frame, byte[] data, int offset, int length) {
			//skipped
		}
		public void onMessage(byte frame, String data) {
			for (ChatWebSocket memberWS : membersWS) {
				try {
					memberWS.channel.sendMessage(frame, data);
				} catch (IOException e) {
					LOG.error("Erro when sending chat message",e);
				}
			}
		}
		public void onDisconnect() {
			membersWS.remove(this);
		}
	}

Configuration client

Dans Google Chrome, pas besoin de framework JavaScript pour coder notre chat. Il reste cependant à utiliser les API fournies par le navigateur. Dans l’exemple ci-dessous, un objet de type WebSocket est créé et les callbacks implémentées

var room = {
        join: function(name) {
          this._username=name;
          var location = "ws://localhost:8080/chat/chat";
          this._ws=new WebSocket(location);
          this._ws.onopen=this._onopen;
          this._ws.onmessage=this._onmessage;
          this._ws.onclose=this._onclose;
        },
            
        send: function(user,message){
          ...
          this._ws.send(user+':'+message);
        },
              
        _onmessage: function(m) {
          if (m.data){
            var c=m.data.indexOf(':');
            var from=m.data.substring(0,c).replace('< ','<').replace('>','>');
            var text=m.data.substring(c+1).replace('< ','<').replace('>','>');
            
            //ajoute les différents éléments à la page HTML
            elementHTML.innerHTML=text;
            ...
          }
        },
...
        
      };

Enfin, on définit l’interaction utilisateur

$('sendB').onclick = function(event) { 
	room.send(//passe le texte saisi par l'utilisateur dans le formulaire ); 
	...
};

Le push est donc de plus en plus accessible. D’autres évolutions sont à prévoir notamment pour contourner la limitation de la Same Origin Policy. Des spécifications W3C sont en cours et ont pour but de pouvoir effectuer des appels Ajax cross domain. Jetty quant à lui propose des implémentations de ce draft.

Sources

4 commentaires sur “Jetty & HTML 5 : les WebSockets sont là !”

  • Pas mal! Pour info, la société Kaazing [http://www.kaazing.com/ et http://www.kaazing.org/] développe une solution de transition, c'est à dire ouverte sur les standards (Web Sockets et Server Sent Events) mais fonctionnant sur quasiment tous les navigateurs du marché (dont IE 5.5+). Leur librairie cliente gérant la communication implémente les interfaces décrites par les specs Web Sockets et SSE. Ma contribution sur le sujet : http://blog.inovia-conseil.fr/?p=61
  • Kaazing est venu présenter sa solution au Ch'ti JUG : http://chtijug.org/session-le-10-decembre-sur-html5-chez-adeo/ Voici les slides de leur session : http://chtijug.org/slides-et-photos-de-la-session-html5/
  • Kaazing, en la personne de Peter Lubbers, était venu à Paris également présenter WebSocket et parler de leur solution à une conférence organisée par Zenika (http://blog.zenika.com/index.php?post/2009/12/14/Conclusion-%22Dans-la-t%C3%AAte-de-Peter-Lubbers%22). Pour info, Peter Lubbers fait parti de l'expert group WebSocket au W3C.
  • @Benoit: Effectivement, la presentation http://blog.zenika.com/public/Billet_0120/peter-lubbers-in-the-brain-websocket.pdf est trés enrichissante. j'ai envie de retenir les choses suivantes: - Concernant les WebSockets Drastic Reduction in network traffic [par rapport à du polling]. * With WebSocket, each frame has only 2 bytes of packaging A ce titre, les chiffres suivants sont intéressants : Example network throughput for Polling HTTP request and response headers: * Use case A: 10,000 clients polling every 60 seconds: Network throughput is (871 x 10,000)/60 = 145,166 bytes = 1,161,328 bits per second (1.1 Mbps) * Use case B: 10,000 clients polling every second: Network throughput is (871 x 10,000) = 8,710,000 bytes = 69,680,000 bits per second (66 Mbps) Et dans le cas du WebSocket Example network throughput for WebSocket HTTP request and response headers: * Use case A: 10,000 frames every 60 seconds: Network throughput is (2 x 10,000)/60 = 333 bytes = 2,664 bits per second (2.6 Kbps) * Use case B: 10,000 frames every second: Network throughput is (2 x 10,000) = 20,000 bytes = 160,000 bits per second (156 Kbps) et donc Overheard… "Reducing kilobytes of data to 2 bytes…and reducing latency from 150ms to 50ms is far more than marginal. In fact, these two factors alone are enough to make WebSocket seriously interesting to Google.“ —Ian Hickson (Google, HTML5 spec lead) J'aime également la partie sur les Server-Sent Events qui permettent d'écouter des topics à la mode JMS ou Adobe Lifecycle HTML5 Server-Sent Events • Standardizes and formalizes how a continuous stream of data can be sent from a server to a browser • Introduces EventSource—a new JavaScript API vu du code, cela donne EventSource API • Connects to a server URL to receive an event stream: var stream = new EventSource("http://news.kaazing.com"); • Set event handlers: stream.onopen = function() { alert(“open”); } stream.onmessage = function(event) { alert(“message: “ + event.data); } stream.onerror = function() { alert(“error”); } Enfin: * des évolutions qui permettraient de faire des requêtes xmlHttpRequest dans des domains différents (là encore, Flex le permet) * des évolutions de xmlHttpRequest qui offre des callbacks supplémentaires sur des évènements de progression de la requete elle même Progress events have fields for: • The total amount of data to transfer • The amount that has already transferred
    1. Laisser un commentaire

      Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *


      Ce formulaire est protégé par Google Recaptcha