Formación HTML5: Websockets

30-08-2013
 

websocket

Una de las nuevas funcionalidades que incluía el HTML5 son los websockets, que nos permiten niveles de interactividad muy altos con el servidor. Para los que quieran formarse en esto hoy implementaremos un websocket entre un servidor Java/Jetty y un cliente web.

El concepto de websocket es el de un socket de toda la vida. Para los que no sepáis lo que es un socket os lo podéis imaginar como un canal directo de comunicación bidireccional entre el navegador y el servidor. ATENCIÓN! La gran ventaja de los websockets radica en el hecho de ser bidireccional, recordad que el protocolo HTTP es unidireccional, es decir, que el cliente hace una petición y recibe una respuesta.

Los principales contras de los websockets es que todavía tienen niveles de soporte muy bajos por parte de navegadores antiguos y dispositivos móviles, de momento ni Opera mini ni Android browser lo aceptan. Por otro lado, la implementación en PHP también es complicada, porque su arquitectura no es muy amigable con esto.

websocket2

Para empezar nuestro proyecto con Websockets+Java+Jetty partiremos de la herramienta maven, que nos ayudará a generar el proyecto. Entonces creamos el proyecto a partir del siguiente pom.xml(mvn clean install):

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">   
    	<groupId>com.lostsys.websocket</groupId>
    	<artifactId>websocket</artifactId>
    	<version>1.0</version>
    	<packaging>war</packaging>
    	<modelVersion>4.0.0</modelVersion>
    	<name>websocket</name>
 	<repositories>
		<repository>
			<id>java.net2</id>
			<name>Repository hosting the jee6 artifacts</name>
			<url>http://download.java.net/maven/glassfish</url>
		</repository>
	</repositories>
	<dependencies>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-web-api</artifactId>
			<version>${webapi.version}</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-server</artifactId>
			<version>${jetty.version}</version>
		</dependency>
		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-websocket</artifactId>
			<version>${jetty.version}</version>
		</dependency>
		<dependency>
			<groupId>org.mortbay.jetty</groupId>
			<artifactId>jetty-maven-plugin</artifactId>
			<version>${jetty.version}</version>
		</dependency>
	</dependencies>
	<build>
        	<plugins>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>${jetty.version}</version>
				<configuration>
				</configuration>
			</plugin>
        	    	<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
    	</build>
	<properties>
		<jetty.version>8.1.12.v20130726</jetty.version>
		<webapi.version>6.0</webapi.version>
	</properties>
</project>

La implementación de un websocket varia en función del servidor de aplicaciones Java que utilicemos, en Jetty deberemos declarar un Websocket(MyWebSocket) que será el que generará los objetos que mantendrán la comunicación(WebSocketMessage):

package com.lostsys.websocket;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
import org.eclipse.jetty.websocket.WebSocket.OnTextMessage;
import org.eclipse.jetty.websocket.WebSocket.Connection;

public class MyWebSocket extends WebSocketServlet {
	private static final long serialVersionUID = 5610501345675935366L;

	public void init() throws ServletException {
		System.out.println("Inicialitzant...");
		super.init();
		}

	protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		getServletContext().getNamedDispatcher("default").forward(request, response);
		super.doGet(request,response);
		}

	@Override
	public WebSocket doWebSocketConnect(HttpServletRequest arg0, String arg1) {
		System.out.println("doWebSocketConnect");
		return new WebSocketMessage();
		}

	public boolean checkOrigin(HttpServletRequest request, String origin) { return true; }
	}

class WebSocketMessage implements OnTextMessage {
	private Connection cn;

	public WebSocketMessage() { }

	public void onMessage(String data) { 
		System.out.println("Message: "+data); 

		try {
			cn.sendMessage("Hola "+data); 
		} catch (Exception ex) { ex.printStackTrace(); }
		}

	@Override
	public void onOpen(Connection c) { cn=c; System.out.println("Connection open."); }

	@Override
	public void onClose(int closeCode, String message) { System.out.println("Connection close."); }
	}

Después tendremos que declarar en el fichero ‘WEB-INF/web.xml‘ nuestro socket:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
	<display-name>websocket</display-name>

  <servlet>
    <servlet-name>Websocket</servlet-name>
    <servlet-class>com.lostsys.websocket.MyWebSocket</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Websocket</servlet-name>
    <url-pattern>/websocket/*</url-pattern>
  </servlet-mapping>

	<welcome-file-list>
    		<welcome-file>index.html</welcome-file>
  	</welcome-file-list>
 
</web-app>

Para finalizar declaramos nuestro ‘index.html’, que abrirá el socket con el servidor.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
	</head>
	<body>
    	<div class="container">
        <h1>My Websocket example.</h1>
        <div>
            <span id="status" class="label label-important">Not Connected</span>
        </div>   
        <label style="display:inline-block">Message: </label><input type="text" id="message"  />
        <button id="send" class="btn btn-primary" onclick="sendMessage()">Send</button>

	<h2>Responses</h2> 
        <div id="received_messages"></div>
    </div>


<script>
var URL = "ws://127.0.0.1:8080/websocket/websocket/";
var websocket;
 
$(document).ready(function(){
    	connect(); 
	});
 
function connect(){
        websocket = new WebSocket(URL);
        websocket.onopen = function(evnt) { onOpen(evnt) };
        websocket.onmessage = function(evnt) { onMessage(evnt) };
        websocket.onerror = function(evnt) { onError(evnt) };
}

function sendMessage() { websocket.send($("#message").val()); }
function onOpen() { $("#status").html("connected") }
function onError(evnt) { alert('\nERROR: ' + evnt.data); }

function onMessage(evnt) {
    	if (typeof evnt.data == "string") {
        	$("#received_messages").append("<p>"+evnt.data+"</p>");
    		}
	}
</script>

	</body>
</html>

Una vez tenemos el proyecto lo compilamos y lo ejecutamos con ‘mvn jetty:run

Ya veis que implementar un web socket es muy sencillo con Java y tiene unas posibilidades enormes en campos como la Gamificación , videojuejos online, aplicaciones HTML5, etc.

El tema de los websockets es un tema complejo, pero espero que esto post haya ayudado a clarificar.

Leave a Reply

© Albert Coronado Calzada