Capítulo 13 API Communication – 3

Capítulo 13 API Communication - 3

13.1   Web Sockets

En esta parte del capítulo, describiremos el último componente de lo que consideramos API Communication. API WebSocket ofrece soporte para comunicaciones bidireccionales entre navegadores y servidores. La comunicación es realizada a través de conexiones TCP, sin enviar cabeceras HTTP, reduciendo de este modo la cantidad de datos transmitidos en cada llamada. Además, la conexión es persistente, permitiendo a los servidores mantener a los navegadores permanentemente informados. Esto significa que no deberemos encargarnos de llamar al servidor a cada momento para obtener datos actualizados; en su lugar, el servidor mismo de forma automática nos enviará información acerca de la situación actual.

WebSocket puede ser considerado por algunos como una mejora de Ajax, pero es en realidad una alternativa totalmente diferente de comunicación que permite la construcción de aplicaciones en tiempo real en una plataforma escalable (por ejemplo, video juegos para múltiples jugadores, salas de chat, etc…).

La API es simple. Solo unos pocos métodos y eventos son incluidos para abrir y cerrar conexiones y enviar y escuchar por mensajes. Sin embargo, ningún servidor soporta este protocolo por defecto. Debido a esto, necesitaremos instalar nuestro propio servidor WS (servidor WebSocket) para poder establecer comunicación entre el navegador y el servidor que aloja a la aplicación.

Configuración del servidor WS

Un programador experimentado seguramente podrá descubrir por sí mismo cómo construir un servidor WS, pero para aquellos que deseamos dedicar nuestro tiempo libre a actividades un tanto más recreativas, ya se encuentran disponibles en la web varios códigos que nos permitirán configurar nuestro propio servidor WS y procesar conexiones en unos pocos minutos. Dependiendo de sus preferencias, puede optar por códigos programados en PHP, Java, Ruby, y otros.

IMPORTANTE: Al momento de escribir estas líneas, la especificación está siendo mejorada y expandida debido a problemas de seguridad y aún no se encuentran servidores WS disponibles que soporten estas mejoras. Para obtener una lista completa, visite nuestro sitio web y siga los enlaces correspondientes a este capítulo.

Los siguientes ejemplos estarán orientados al uso de un servidor sencillo programado en PHP llamado phpwebsocket (code.google.com/p/phpwebsocket/). Este servidor utiliza un único archivo llamado server.php que responde a una serie de códigos pre programados, como veremos más adelante. El archivo debe ser subido a un servidor y luego ejecutado por medio de un sistema del tipo Telnet como Putty, por ejemplo.

WebSocket usa una conexión persistente, por lo que el código del servidor WS tiene que funcionar todo el tiempo, capturando solicitudes y enviando actualizaciones a los navegadores conectados. Usando Putty puede acceder a su servidor desde una consola y ejecutar los comandos necesarios para poner el servidor WS en marcha.

Hágalo usted mismo: En primer lugar debe contar con las herramientas adecuadas. Instale su consola de acceso Telnet o descargue Putty desde www.chiark. greenend.org.uk/~sgtatham/putty/. También necesita descargar el archivo server.php del servidor phpwebsocket disponible en http://code.google.com/ p/phpwebsocket/. Modifique los datos de acceso dentro de este archivo (dominio o IP de su servidor y puerto), y súbalo a su servidor. Desde la consola Telnet acceda al servidor y ejecute el archivo con el siguiente comando: php -q server.php. Esto pondrá en marcha el servidor WS.

IMPORTANTE: El servidor no solo se encarga de establecer la comunicación entre el navegador y el servidor sino que además está a cargo de generar la respuesta adecuada. La forma de construir y realizar esta respuesta está programada dentro del mismo código del servidor. Deberá adaptar este código a las necesidades de su aplicación.

Constructor

Antes de programar los códigos para interactuar con el servidor WS, veamos lo que la API ofrece con este fin. La especificación declara solo una interface con unos pocos métodos, propiedades y eventos, además de un constructor, para establecer la conexión:

WebSocket(url) Este constructor inicia una conexión entre la aplicación y el servidor WS apuntado por el atributo url. Retorna un objeto WebSocket referenciando esta conexión. Un segundo atributo puede ser especificado para proveer un array con sub protocolos de comunicación.

Métodos

La conexión es iniciada por el constructor, por lo que solo necesitamos dos métodos para utilizarla:

send(datos) Este es el método necesario para enviar un mensaje al servidor WS. El valor del atributo datos representa los datos a ser transmitidos (normalmente una cadena de texto).

close() Este método cierra la conexión.

Propiedades

Algunas propiedades no permiten conocer la configuración y el estado de la conexión:

url Muestra la URL a la cual la aplicación está conectada. protocol Esta propiedad retorna el sub protocolo usado, si existe.

readyState Esta propiedad retorna un número representando el estado de la conexión: 0 significa que la conexión no ha sido aún establecida, 1 significa que la conexión fue abierta, 2 significa que la conexión está siendo cerrada, y 3 significa que la conexión fue cerrada.

bufferedAmount Esta es una propiedad extremadamente útil que nos permite conocer la cantidad de datos requeridos pero aún no enviados al servidor. El valor retornado nos ayuda a regular la cantidad de datos y la frecuencia de cada solicitud para evitar saturar al servidor.

Eventos

Para conocer el estado de la conexión y escuchar por mensajes enviados por el servidor, debemos usar eventos. La API ofrece los siguientes:

open Este evento es disparado cuando la conexión es abierta.

message Este evento es disparado cuando un mensaje proveniente del servidor se encuentra disponible.

error Este evento es disparado cuando ocurre un error. close Este evento es disparado cuando la conexión es cerrada.

Plantilla

El archivo server.php del servidor WS que usamos como ejemplo contiene una función process () que procesa una pequeña lista de comandos predefinidos y envía de regreso la respuesta apropiada. Para probar esta API, vamos a usar un formulario en el que podremos ingresar uno de estos comandos y enviarlos al servidor:

<!DOCTYPE html>

<html lang=»es»>

<head>

<title>WebSocket</title>

<link rel=»stylesheet» href=»websocket.css»>

<script src=»websocket.js»></script>

</head>

<body>

<section id=»cajaformulario»>

<form name=»formulario»>

<p>Comando:<br><input type=»text» name=»comando»

id=»comando»></p>

<p><input type=»button» name=»boton» id=»boton»

value=»Enviar»></p>

</form>

</section>

<section id=»caj adatos»></section>

</body>

</html>

Listado 13-20. Insertando commandos.

También crearemos un archivo CSS llamado websocket.css con los siguientes estilos:

#caj aformulario{
float: left;

padding: 20px;

border: 1px solid #999999;

}

#caj adatos{

float: left;

width: 500px;

height: 350px;

overflow: auto; margin-left: 20px;

padding: 20px;

border: 1px solid #999999;

}

Listado 13-21. Estilos habituales para las cajas.

Iniciar la comunicación

Como siempre, el código Javascript es responsable de todo el proceso. En el siguiente listado, crearemos nuestra primera aplicación de comunicaciones para entender la forma de trabajo de esta API:

function iniciar(){

caj adatos=document.getElementById(‘caj adatos’); var boton=document.getElementById(‘boton’); boton.addEventListener(‘click’, enviar, false);

socket=new WebSocket(«ws://www.dominio.com:12345/server.php«); socket.addEventListener(‘message’, recibido, false);

}

function recibido(e){

var lista=cajadatos.innerHTML;

caj adatos.innerHTML=’Recibido:    ‘+e.data+'<br>’+lista;

}

function enviar(){

var comando=document.getElementById(‘comando’).value; socket.send(comando);

}

window.addEventListener(‘load’, iniciar, false);

Listado 13-22. Enviando mensajes al servidor.

En la función iniciar(), el objeto WebSocket es construido y almacenado en la variable socket. El atributo url del constructor apunta hacia la ubicación del archivo server.php en nuestro servidor, incluyendo el puerto de conexión (en este ejemplo, 12345). Generalmente la dirección del servidor WS será declarada con el valor IP correspondiente al servidor (en lugar de un dominio) y un valor de puerto como 8000 u 8080, pero esto dependerá de sus necesidades, la configuración de su servidor, la ubicación de su archivo, etc… El uso de la IP en lugar del dominio es una práctica recomendada para evitar el proceso de traducción DNS. En cada una de las llamadas, la red realiza un proceso de traducción de las direcciones web para obtener las direcciones reales de los servidores a los que corresponden. Si en lugar de especificar un dominio declaramos directamente la dirección IP de nuestro servidor, evitamos esta operación, estableciendo una comunicación más fluida.

Luego de que obtenemos el objeto WebSocket, una escucha para el evento message es agregada. Este evento será disparado cada vez que el servidor WS envíe un mensaje al navegador. La función recibido() fue declarada para responder al mismo. Como en otras API, este evento también incluye la propiedad data que retorna el contenido del mensaje. En la función recibido (), usamos esta propiedad para mostrar el mensaje en pantalla.

Para enviar mensajes al servidor incluimos la función enviar(). El valor insertado en el elemento <input> llamado comando es tomado por esta función y enviado al servidor WS usando el método send ().

IMPORTANTE: El archivo server.php contiene una función llamada process () para procesar cada llamada y enviar una respuesta acorde a los datos recibidos. Usted puede cambiar esta función para satisfacer las necesidades de su aplicación, pero para nuestros ejemplos la hemos considerado exactamente como es ofrecido en Google Codes. La función toma el valor del mensaje recibido y lo compara con una lista de comandos predefinidos. Los comandos disponibles en la versión que utilizamos para probar estos ejemplos son: hello, hi, name, age, date, time, thanks, y bye. Por ejemplo, si enviamos el mensaje «hello», el servidor nos responderá «hello human».

Aplicación completa

En nuestro primer ejemplo podemos ver cómo funciona el proceso de comunicación de esta API. El constructor WebSocket inicia la conexión, el método send() envía cada mensaje al servidor para ser procesado, y el evento message informa a la aplicación sobre las respuestas recibidas. Sin embargo, no cerramos la conexión, no controlamos por errores, e incluso no detectamos cuándo la conexión estaba lista para trabajar. Veamos ahora un ejemplo que aprovecha todos los eventos provistos por la API para informar sobre el estado de la conexión en cada paso del proceso.

function iniciar(){

caj adatos=document.getElementById(‘caj adatos’);

var boton=document.getElementById(‘boton’);

boton.addEventListener(‘click’, enviar, false);

socket=new WebSocket(«ws://www.dominio.com:12345/server.php«); socket.addEventListener(‘open’, abierto, false);

socket.addEventListener(‘message’, recibido, false);

socket.addEventListener(‘close’, cerrado, false); socket.addEventListener(‘error’, errores, false);

}

function abierto(){

caj adatos.innerHTML=’CONEXION ABIERTA<br>’;

caj adatos.innerHTML+=’Estado: ‘+socket.readyState;

}

function recibido(e){

var lista=cajadatos.innerHTML;

caj adatos.innerHTML=’Recibido:    ‘+e.data+'<br>’+lista;

}

function cerrado(){

var lista=cajadatos.innerHTML;

caj adatos.innerHTML=’CONEXION CERRADA<br>’+lista;

var boton=document.getElementById(‘boton’);

boton.disabled=true;

}

function errores(){

var lista=cajadatos.innerHTML;

caj adatos.innerHTML=’ERROR<br>’+lista;

}

function enviar(){

var comando=document.getElementById(‘comando’).value;

if(comando==’cerrar’){ socket.close();

}else{

socket.send(comando);

}

}

window.addEventListener(‘load’, iniciar, false);

Listado 13-23. Informando el estado de la conexión.

Incluimos algunas mejoras en el código del Listado 13-23 comparado con el ejemplo anterior. Escuchas para todos los eventos disponibles en el objeto WebSocket fueron agregadas junto con las funciones apropiadas para responder a cada uno de ellos. También mostramos el estado de la conexión cuando es abierta usando el valor de la propiedad readyState, cerramos la conexión usando el método close() cuando el comando «cerrar» es enviado por el usuario desde el formulario, y desactivamos el botón «Enviar» cuando la conexión es cerrada (boton.disabled=true).

Hágalo usted mismo: Este último ejemplo requiere el documento HTML y los estilos CSS de los Listados 13-20 y 13-21. Suba estos archivos a su servidor y, si aún no lo ha hecho, ejecute el servidor WS con la instrucción php -q server.php (como ya explicamos, esto es realizado desde una consola Telnet como Putty). Una vez que el servidor es activado, abra el documento HTML en su navegador. Inserte comandos en el formulario en pantalla y presione el botón «Enviar». Debería obtener respuestas del servidor de acuerdo al comando insertado (hello, hi, name, age, date, time, thanks, o bye). Envíe el comando «cerrar» para cerrar la conexión.

IMPORTANTE: Si el servidor no funciona correctamente, los errores producidos serán retornados dentro de la consola Telnet. Controle esta consola para descubrir inconvenientes en la conexión.

13.2    Referencia rápida

HTML5 incorpora tres API diferentes con propósitos de comunicación. XMLHttpRequest Level 2 es una mejora del viejo objeto XMLHttpRequest usado para la construcción de aplicaciones Ajax. La API Web Messaging ofrece un sistema de comunicación para diferentes ventanas, pestañas, iframes, o incluso otras APIs. Y la API WebSocket provee una nueva alternativa para establecer una conexión rápida y efectiva entre navegadores y servidores.

XMLHttpRequest Level 2

Esta API tiene un constructor para objetos XMLHttpRequest y algunos métodos, propiedades y eventos para procesar la conexión.

XMLHttpRequest() Este constructor retorna el objeto XMLHttpRequest que necesitamos para iniciar y procesar una conexión con el servidor. open(método, url, asíncrono) Este método abre la conexión entre la aplicación y el servidor. El atributo método determina qué método HTTP será usado para enviar la información (get o post). El atributo url declara la ruta hacia el código que recibirá y procesará esta información. Y el atributo asíncrono es un valor booleano que establece si la conexión será síncrona o asíncrona (true para asíncrona). send(datos) Este método envía el valor del atributo datos al servidor. El atributo datos puede ser un ArrayBuffer, un blob, un documento, una cadena de texto o un objeto FormData.

abort() Este método cancela la solicitud.

timeout Esta propiedad establece el tiempo en milisegundos que tiene la solicitud para ser procesada.

readyState Esta propiedad retorna un valor representando el estado de la conexión: 0 significa que el objeto fue construido, 1 significa que la conexión fue abierta, 2 significa que la cabecera de la respuesta ha sido recibida, 3 significa que la respuesta está siendo recibida, 4 significa que la transmisión de datos ha sido completada. responseType Esta propiedad retorna el tipo de respuesta. Puede ser declarada para cambiar el tipo de respuesta. Los posibles valores son arraybuffer, blob, document, y text.

response Esta propiedad retorna la respuesta a la solicitud en el formato declarado por la propiedad responseType.

responseText Esta propiedad retorna la respuesta a la solicitud en formato texto. responseXML Esta propiedad retorna la respuesta a la solicitud como si fuera un documento XML.

loadstart Este evento es disparado cuando la solicitud es iniciada.

progress Este evento es disparado periódicamente mientras la solicitud es procesada.

abort Este evento es disparado cuando la solicitud es abortada.

error Este evento es disparado cuando ocurre un error.

load Este evento es disparado cuando la solicitud ha sido completada exitosamente. timeout Este evento es disparado cuando la solicitud toma más tiempo en ser procesada que el especificado por la propiedad timeout. loadend Este evento es disparado cuando la solicitud ha sido completada (en ambos casos, éxito o error).

Un atributo especial fue incluido para obtener un objeto XMLHttpRequestUpload en lugar del objeto XMLHttpRequest con el propósito de subir datos al servidor.

upload Este atributo retorna un objeto XMLHttpRequestUpload. Este objeto usa los mismos métodos, propiedades y eventos de un objeto XMLHttpRequest pero con el propósito de procesar la subida de archivos.

La API también incluye una interface para crear objetos FormData representando formularios HTML.

FormData() Este constructor retorna un objeto FormData para representar un formulario HTML.

append(nombre, valor) Este método agrega datos a un objeto FormData. Cada dato agregado al objeto representa un campo de formulario, con su nombre y valor declarado en los atributos. Una cadena de texto o un blob pueden ser provistos para el atributo valor.

Esta API usa la interface ProgressEvent (también usada por otras APIs para controlar el progreso de una operación) que incluye las siguientes propiedades:

lengthComputable Esta propiedad es un valor booleano que determina si los valores del resto de las propiedades son válidos.

loaded Esta propiedad retorna la cantidad total de bytes ya descargados o subidos. total Esta propiedad retorna el tamaño total en bytes de los datos que están siendo descargados o subidos.

API Web Messaging

Esta API está constituida solo por una interface que provee métodos, propiedades y eventos para comunicar entre sí aplicaciones ubicadas en diferentes ventanas, pestañas, iframes o incluso otras API.

postMessage(mensaje, destino) Este método envía un mensaje a una contentwindow específica y al documento declarado como destino por el atributo destino. El atributo mensaj e es el mensaje a ser transmitido. message Este evento es disparado cuando un mensaje es recibido. data Esta propiedad del evento message retorna el contenido del mensaje recibido. origin Esta propiedad del evento message retorna el origen del documento que envió el mensaje.

source Esta propiedad del evento message retorna una referencia a la ventana desde la que el mensaje fue enviado.

API WebSocket

Esta API incluye un constructor que retorna un objeto WebSocket e inicia la conexión. Además, provee algunos métodos, propiedades y eventos para controlar la comunicación entre el navegador y el servidor.

WebSocket(url) Este constructor retorna un objeto WebSocket e inicia la conexión con el servidor. El atributo url declara la ruta del código del servidor WS y el puerto de comunicación. Un array con sub protocolos puede ser especificado como un segundo atributo.

send(datos) Este método envía un mensaje al servidor WS. El atributo datos debe ser una cadena de texto con el mensaje a ser enviado. close() Este método cierra la conexión con el servidor WS.

url Esta propiedad muestra la URL que la aplicación está usando para conectarse al servidor WS.

protocol Esta propiedad retorna el sub protocolo usado por la conexión, si existe. readyState Esta propiedad retorna un valor representando el estado de la conexión: 0 significa que la conexión no ha sido aún establecida, 1 significa que la conexión fue abierta, 2 significa que la conexión está siendo cerrada, y 3 significa que la conexión fue cerrada.

bufferedAmount Esta propiedad retorna la cantidad total de datos que esperan ser enviados al servidor.

open Este evento es disparado cuando la conexión es abierta.

message Este evento es disparado cuando el servidor envía un mensaje a la aplicación. error Este evento es disparado cuando ocurre un error. close Este evento es disparado cuando la conexión es cerrada.

Publicaciones Similares