Está en la página 1de 89

Página 1

Andrew Lombardi

WebSock
et
COMUNICACIONES LIGERAS CLIENTE-SERVIDOR
www.it-ebooks.info

Página 2
JAVASCRIPT / IDIOMAS DE PROGRAMACIÓN

WebSocket
ISBN: 978-1-449-36927-9
Nosotros $ 24,99
CAN $ 28,99

" Este libro explica


una serie de útiles
ejemplos, de fácil aplicación
al mundo real, junto
con discusiones de
problemas que los desarrolladores
encontrará al trabajar
con WebSocket
protocolo. "
—Joseph B. Ottinger
Ingeniero sénior, Edifecs, Inc.

" Una introducción completa


a los conceptos de WebSocket
e implementación
detalles. "
—Arun Gupta
Director de defensa de desarrolladores, Red Hat
Gorjeo: @oreillymedia
facebook.com/oreilly
Hasta hace poco, crear aplicaciones de escritorio en el navegador significaba
utilizando tecnologías ineficientes Ajax o Comet para comunicarse con el
servidor. Con esta guía práctica, aprenderá a utilizar WebSocket, un
protocolo que permite al cliente y al servidor comunicarse con cada
otros en una sola conexión simultáneamente . No más asincrónico
comunicación o votación larga!
Para los desarrolladores con un buen conocimiento de JavaScript (y quizás Node.js),
El autor Andrew Lombardi proporciona ejemplos prácticos útiles a lo largo de
el libro para ayudarle a ponerse al día con la API de WebSocket. Tu tambien
aprenda a usar WebSocket con Transport Layer Security (TLS).
■ Aprenda a utilizar los eventos, mensajes, atributos y atributos de la API de WebSocket.
y métodos dentro de su aplicación cliente
■ Cree aplicaciones de chat bidireccionales en el cliente y el servidor.
con WebSocket como capa de comunicación
■ Cree un subprotocolo sobre WebSocket para STOMP 1.0, el
Protocolo simple de mensajería orientada a texto
■ Utilice opciones para navegadores más antiguos que no son compatibles de forma nativa
WebSocket
■ Proteja su aplicación WebSocket contra varios ataques
vectores con TLS y otras herramientas
■ Depurar aplicaciones aprendiendo aspectos de WebSocket
ciclo vital
Andrew Lombardi, propietario de la consultora Mystic Coders, ha pasado el pasado
seis años dando decenas de charlas en conferencias en toda América del Norte y Europa
sobre temas que van desde el desarrollo backend de Java y HTML5 hasta la creación de
móvil utilizando solo JavaScript.
www.it-ebooks.info

Página 3
Andrew Lombardi
Bostón

WebSocket
www.it-ebooks.info

Página 4
978-1-449-36927-9
[LSI]
WebSocket
por Andrew Lombardi
Derechos de autor © 2015 Mystic Coders, LLC. Todos los derechos reservados.
Impreso en los Estados Unidos de América.
Publicado por O'Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.
Los libros de O'Reilly se pueden comprar con fines educativos, comerciales o promocionales de ventas. Las ediciones en línea son
también disponible para la mayoría de los títulos (http://safaribooksonline.com ). Para más información, contacte con nuestro corporativo /
departamento de ventas institucional: 800-998-9938 o corporate@oreilly.com .
Editores: Simon St. Laurent y Brian MacDonald
Editor de producción: Colleen Lobner
Corrector de estilo: Kim Cofer
Correctora de pruebas: Sharon Wilkey
Indexadora: Wendy Catalano
Diseñador de interiores: David Futato
Diseñador de portada: Karen Montgomery
Ilustradora: Rebecca Demarest
Septiembre de 2015:
Primera edición
Historial de revisiones de la primera edición
2015-09-04: Primera versión
Consulte http://oreilly.com/catalog/errata.csp?isbn=9781449369279 para obtener detalles sobre la versión.
El logotipo de O'Reilly es una marca registrada de O'Reilly Media, Inc. WebSocket, la imagen de portada de un mar
anemone y la imagen comercial relacionada son marcas comerciales de O'Reilly Media, Inc.
Si bien el editor y el autor han realizado esfuerzos de buena fe para garantizar que la información y
Las instrucciones contenidas en este trabajo son precisas, el editor y el autor renuncian a toda responsabilidad.
por errores u omisiones, incluida, entre otras, la responsabilidad por los daños resultantes del uso de
o confianza en este trabajo. El uso de la información y las instrucciones contenidas en este trabajo es por su cuenta
riesgo. Si alguna muestra de código u otra tecnología que este trabajo contiene o describe está sujeta a código abierto
licencias o los derechos de propiedad intelectual de otros, es su responsabilidad asegurarse de que su uso
del mismo cumple con dichas licencias y / o derechos.
www.it-ebooks.info

Página 5

Tabla de contenido
Prefacio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . ix
1. Inicio rápido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
........1
Obteniendo Node y npm
2
Instalación en Windows
2
Instalación en OS X
2
Instalación en Linux
2
¡Hola Mundo! Ejemplo
3
¿Por qué WebSocket?
7
Resumen
8
2. API de
WebSocket. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
.9
Inicializando
9
IU de ejemplo de stock
11
Eventos de WebSocket
12
Evento: Abierto
13
Evento: Mensaje
14
Evento: error
15
Evento: PING / PONG
15
Evento: Cerrar
15
Métodos de WebSocket
dieciséis
Método: enviar
dieciséis
Método: Cerrar
17
Atributos de WebSocket
18
Atributo: readyState
18
Atributo: bufferAmount
19
Atributo: protocolo
19
Servidor de ejemplo de stock
19
iii
www.it-ebooks.info

Página 6
Prueba de compatibilidad con WebSocket
21
Resumen
21
3. Chat
bidireccional. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
3
Sondeo largo
23
Escribir una aplicación de chat básica
24
Cliente WebSocket
27
Identidad del cliente
27
Eventos y notificaciones
29
El servidor
30
El cliente
31
Resumen
34
4. PISE a través de
WebSocket. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Implementación de STOMP
36
Conectarse
36
Conexión a través del servidor
39
Configuración de RabbitMQ
42
Conexión del servidor a RabbitMQ
44
El demonio del precio de las acciones
47
Procesamiento de solicitudes de STOMP
49
Cliente
50
Usando RabbitMQ con Web-Stomp
56
Cliente STOMP para Web y Node.js
57
Instalación del complemento Web-Stomp
57
Cliente Echo para Web-Stomp
57
Resumen
59
5. Compatibilidad con
WebSocket. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
SockJS
62
Servidor de chat SockJS
63
Cliente de chat SockJS
66
Socket.IO
66
Adobe Flash Socket
67
Conectando
67
Servidor de chat Socket.IO
68
Cliente de chat Socket.IO
69
Pusher.com
70
Canales
71
Eventos
72
Servidor de chat Pusher
73
Cliente de chat Pusher
76
iv | Tabla de contenido
www.it-ebooks.info

Página 7
No se olvide: Pusher es una solución comercial
78
Proxy inverso
78
Resumen
78
6. Seguridad de
WebSocket. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
TLS y WebSocket
79
Generación de un certificado autofirmado
79
Instalación en Windows
80
Instalación en OS X
80
Instalación en Linux
80
Configuración de WebSocket sobre TLS
80
Ejemplo de servidor WebSocket sobre TLS
82
Modelo de seguridad basado en el origen
83
Clickjacking
85
Opciones de X-Frame para Framebusting
86
Negación de servicio
87
Enmascaramiento de marcos
87
Validación de clientes
88
Configurar dependencias e inicios
88
Escuchar solicitudes web
89
Servidor WebSocket
91
Resumen
92
7. Depuración y
herramientas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
El apretón de manos
95
El servidor
96
El cliente
97
Descargar y configurar ZAP
99
WebSocket Secure to the Rescue
102
Validación del apretón de manos
102
Inspección de marcos
103
Cargas útiles enmascaradas
103
Conexión de cierre
108
Resumen
109
8. Protocolo
WebSocket. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
HTTP 0.9: Nace la Web
111
HTTP 1.0 y 1.1
111
Apretón de manos abierto de WebSocket
112
Sec-WebSocket-Key y Sec-WebSocket-Accept
113
Encabezados HTTP de WebSocket
114
Marco WebSocket
116
Tabla de contenido | v
www.it-ebooks.info

Página 8
Poco de aleta
117
Códigos de operación de marco
117
Enmascaramiento
118
Longitud
118
Fragmentación
119
Apretón de manos de WebSocket Close
119
Subprotocolos de WebSocket
121
Extensiones de WebSocket
122
Implementaciones de servidores alternativos
123
Resumen
124
Índice. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . 125
vi | Tabla de contenido
www.it-ebooks.info

Página 9
Por Joaquín
www.it-ebooks.info

Página 10
www.it-ebooks.info

Página 11

Prefacio
La Web ha crecido.
En los viejos tiempos, solíamos codificar sitios web ricos en diseño utilizando un desorden interminable
de
mesas. Hoy podemos utilizar un enfoque basado en estándares con hojas de estilo en cascada
(CSS) para lograr diseños que no eran posibles en la infancia de la Web. Así como CSS marcó el
comienzo de una
nueva era de capacidad y legibilidad para los aspectos de diseño de un sitio, WebSocket puede hacer
el de la comunicación bidireccional con el backend.
WebSocket proporciona un enfoque basado en estándares para la codificación de bidireccionales full-
duplex.
comunicación nacional que reemplaza los trucos ancestrales como Comet y las encuestas largas.
Hoy tenemos la capacidad de crear aplicaciones de escritorio en un navegador sin
recurriendo a métodos que agotan los recursos del lado del servidor.
En este libro, aprenderá las formas sencillas de cumplir con la comunicación bidireccional
entre el servidor y el cliente, y hágalo sin hacer llorar al informático.

Quién debería leer este libro


Este libro es para programadores que desean crear aplicaciones web que puedan comunicarse
nicarse bidireccionalmente entre el servidor y el cliente y quiénes buscan evitar el uso
hacks que prevalecen en la Web hoy en día. La promesa de WebSocket es una mejor manera,
basado en estándares y compatible con todos los navegadores modernos, con una alternativa sensible
opciones para aquellos que necesitan apoyarlo. Para aquellos que no han considerado Web‐
Socket, deja el tutorial de Comet que has estado leyendo.
Este libro es apropiado para principiantes y usuarios experimentados. Supongo que tienes un
conocimientos de programación y están familiarizados con JavaScript. Experiencia con Node.js
es útil, pero no obligatorio. Este libro también beneficiará a los encargados de
mantener servidores que ejecutan código WebSocket, y son responsables de garantizar la
seguridad de la infraestructura. Necesita conocer las posibles dificultades de integrar
WebSocket y lo que eso significa para ti. Los capítulos anteriores pueden ser de menor utilidad para
ix
www.it-ebooks.info

Pagina 12
usted, pero los últimos tres capítulos le darán suficiente conocimiento para saber lo que
en toda su red.

Objetivos de este libro


He estado en las trincheras y he tenido que implementar hacks aceptables para lograr
comunicación bidireccional para clientes que necesitaban la funcionalidad. Es mi esperanza
que puedo mostrarte una mejor manera, una que se base en estándares y resulte simple para
implementar. Para varios clientes a lo largo de los años, he implementado con éxito el
enfoque para comunicarse con el backend mediante WebSocket en lugar de long
encuestas y he logrado los objetivos que perseguía.

Navegando por este libro


A menudo leo un libro hojeando y sacando las piezas relevantes para usar como referencia
ence durante la codificación. Si realmente está leyendo este prefacio, la siguiente lista le dará
tienes una idea aproximada de los objetivos de cada capítulo:
• Los capítulos 1 y 2 proporcionan una guía de inicio rápido con instrucciones sobre las dependencias.
necesario a lo largo del libro y le presenta la API de JavaScript.
• El Capítulo 3 presenta un ejemplo completo con código de cliente y servidor usando chat.
• En el Capítulo 4 , escribe su propia implementación de un protocolo estándar y
colóquelo encima de WebSocket.
• El Capítulo 5 es esencial para quienes necesitan admitir navegadores más antiguos.
• Por último, los capítulos 6 al 8 se sumergen en aspectos de seguridad, depuración y
descripción general del protocolo.

Las convenciones usadas en este libro


En este libro se utilizan las siguientes convenciones tipográficas:
Itálico
Indica nuevos términos, URL, direcciones de correo electrónico, nombres de archivo y extensiones de
archivo.
Ancho constante
Se utiliza para listados de programas, así como dentro de párrafos para referirse a los elementos del
programa.
mentos tales como nombres de variables o funciones, bases de datos, tipos de datos, entorno
variables, declaraciones y palabras clave.
Ancho constante en negrita
Muestra comandos u otro texto que el usuario debe escribir literalmente.
x | Prefacio
www.it-ebooks.info

Página 13
Cursiva de ancho constante
Muestra texto que debe reemplazarse por valores proporcionados por el usuario o por valores
minado por el contexto.
Este elemento significa una nota general.

Usar ejemplos de código


El material complementario (ejemplos de código, ejercicios, etc.) está disponible para descargar en
https://github.com/kinabalu/websocketsbook.
Este libro está aquí para ayudarlo a hacer su trabajo. En general, si se ofrece un código de ejemplo
con este libro, puede utilizarlo en sus programas y documentación. Tu no
debe comunicarse con nosotros para obtener permiso a menos que esté reproduciendo una parte
significativa de
el código. Por ejemplo, escribir un programa que use varios fragmentos de código de este
libro no requiere permiso. Vender o distribuir un CD-ROM de ejemplos
de los libros de O'Reilly requiere permiso. Responder una pregunta citando esto
El código de ejemplo de libro y cita no requiere permiso. Incorporando un significado
cantidad de código de ejemplo de este libro en la documentación de su producto no
requieren permiso.
Agradecemos la atribución, pero no la exigimos. Una atribución generalmente incluye el
título, autor, editor e ISBN. Por ejemplo: " WebSocket de Andrew Lombardi
(O'Reilly). Copyright 2015 Mystic Coders, LLC, 978-1-4493-6927-9 ”.
Si cree que el uso que hace de los ejemplos de código queda fuera del uso legítimo o del permiso
otorgado
arriba, no dude en contactarnos en permissions@oreilly.com .

Libros en línea de Safari®


Safari Books Online es una biblioteca digital bajo demanda que entrega
contenido experto en libros y videos del
autores líderes mundiales en tecnología y negocios.
Profesionales de la tecnología, desarrolladores de software, diseñadores web y empresas y creación
tivos profesionales utilizan Safari Books Online como su principal recurso para la investigación,
capacitación en resolución de problemas, aprendizaje y certificación.
Safari Books Online ofrece una variedad de planes y precios para empresas , gobiernos ,
educación e individuos.
Prefacio | xi
www.it-ebooks.info

Página 14
Los miembros tienen acceso a miles de libros, videos de capacitación y prepublicación
manuscritos en una base de datos de búsqueda completa de editores como O'Reilly Media,
Prentice Hall Professional, Addison-Wesley Professional, Microsoft Press, Sams, Que,
Peachpit Press, Focal Press, Cisco Press, John Wiley & Sons, Syngress, Morgan Kauf‐
mann, IBM Redbooks, Packt, Adobe Press, FT Press, Apress, Manning, New Riders,
McGraw-Hill, Jones & Bartlett, Course Technology y cientos más . Para más
información sobre Safari Books Online, visítenos en línea.

Cómo contactarnos
Dirija sus comentarios y preguntas sobre este libro al editor:
O'Reilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (en los Estados Unidos o Canadá)
707-829-0515 (internacional o local)
707-829-0104 (fax)
Tenemos una página web para este libro, donde enumeramos erratas, ejemplos y cualquier
información. Puede acceder a esta página enhttp://bit.ly/orm-websocket.
Para comentar o hacer preguntas técnicas sobre este libro, envíe un correo electrónico a bookques‐
tions@oreilly.com .
Para obtener más información sobre nuestros libros, cursos, conferencias y noticias, consulte nuestra web‐
sitio en http://www.oreilly.com.
Encuentranos en Facebook: http://facebook.com/oreilly
Síganos en Twitter: http://twitter.com/oreillymedia
Míranos en YouTube: http://www.youtube.com/oreillymedia

Expresiones de gratitud
Mucha gente hizo posible este libro, incluido mi maravilloso y paciente editor
Brian MacDonald. Para todos en O'Reilly que ayudaron a hacer realidad este libro, un
profundo y profundo agradecimiento.
También me gustaría agradecer a mis revisores técnicos por su invaluable contribución y
consejo: Joe Ottinger y Arun Gupta. Y gracias a aquellos de ustedes que enviaron erratas
en la vista previa del libro para que podamos resolverlos antes de pasar a producción.
Gracias a mamá y papá, por ponerme una computadora frente a mí y abrir un
universo de creatividad y asombro en constante expansión.
xii | Prefacio
www.it-ebooks.info

Página 15
CAPÍTULO 1
Inicio rápido
La API y el protocolo de WebSocket se definen en RFC 6455. WebSocket te ofrece una
canal de comunicaciones bidireccional, full-duplex que opera sobre HTTP a través de
un solo enchufe.
Los hacks existentes que se ejecutan a través de HTTP (como sondeos largos) envían solicitudes a
intervalos,
independientemente de si los mensajes están disponibles, sin ningún conocimiento del estado de
el servidor o cliente. Sin embargo, la API de WebSocket es diferente: el servidor y el cliente
tienen una conexión abierta desde la que pueden enviar mensajes de ida y vuelta. Para el
con mentalidad de seguridad, WebSocket también opera sobre Transport Layer Security (TLS) o
Secure Sockets Layer (SSL) y es el método preferido, como verá en el Capítulo 6 .
Este libro lo ayudará a comprender la API de WebSocket, el protocolo y cómo usar
en sus aplicaciones web hoy.
En este libro, JavaScript se utiliza para todos los ejemplos de código. Node.js se usa para todos los
servidores
código, y para código de cliente ocasional o pruebas cuando un navegador es ajeno a obtener un
sentido de funcionalidad. Para comprender los ejemplos, necesitará cierto nivel de profi‐
competencia con JavaScript. Si desea estudiar más sobre JavaScript, le recomiendo Douglas
JavaScript de Crockford : las buenas partes (O'Reilly).
Node.js ha prevalecido tanto en los últimos años que las barreras de entrada
porque los ejemplos de este libro son notablemente bajos. Si ha realizado algún desarrollo para
la Web, es muy probable que haya desarrollado en JavaScript o al menos que comprenda
eso. El uso de Node.js y JavaScript en todas partes, entonces, está destinado solo a simplificar el
proceso de enseñanza, y no debe interpretarse como un requisito para un WebSocket
proyecto.
Hay bibliotecas y servidores disponibles que admiten WebSocket en casi todos los
configuración. El Capítulo 5 cubre varias opciones para implementar un WebSocket
1
www.it-ebooks.info

Página 16
servidor, incluidos los métodos alternativos para los clientes que no ofrecen soporte para esta tecnología
ogy todavía.

Obteniendo Node y npm


Para asegurarse de que puede ejecutar todos los ejemplos del libro, le recomiendo encarecidamente
que instale Node.js y npm en su entorno de desarrollo. Mientras puedas
aprender todo sobre WebSocket sin tocar ningún código, no lo recomiendo. Los siguientes
Las secciones siguientes indican algunas formas sencillas de obtener Node en su entorno.
Instalación en Windows
Solo cubro la descarga e instalación del binario precompilado disponible en Win‐
dows. Si eres masoquista y te gustaría compilarlo tú mismo, puedes seguir las
instrucciones.
Para el resto de ustedes, descarguen el ejecutable independiente de Windows . Entonces agarra el lat
archivo .zip est de npm. Desempaquete el npm .zip y coloque el node.exe descargado en un
directorio que agrega a su RUTA . Podrás ejecutar scripts e instalar módulos usando
Node.js y npm, respectivamente.
Instalación en OS X
Dos de los métodos más fáciles de instalar Node.js y npm son a través de un precompilado
paquete descargable, o mediante un administrador de paquetes. Mi preferencia es usar un paquete
administrador como Homebrew para obtener Node.js en su máquina. Esto permite una rápida y
actualización sencilla sin tener que volver a descargar un paquete de la Web. Asumiendo que
tiene Homebrew instalado, ejecute este comando:
brew install nodo
Y si prefiere utilizar los binarios precompilados disponibles, puede encontrar la descarga
en el sitio de Node.js. Cuando desee instalar una versión actualizada de Node.js, abajo‐
cargue e instale el último paquete y sobrescribirá los binarios existentes.
Instalación en Linux
Debido a que hay más sabores de Linux que estrellas en el cielo, solo describiré cómo
compílelo usted mismo y cómo obtenerlo a través de apt en Ubuntu . Si estás corriendo otro
distro y me gustaría usar el administrador de paquetes disponible en su sabor particular,
visita el Wiki de Node.js para obtener instrucciones sobre la instalación.
2 | Capítulo 1: Inicio rápido
www.it-ebooks.info

Página 17
El uso de apt para instalar Node.js requiere unos simples pasos:
sudo apt-get update
sudo apt-get install python-software-properties python g ++ make
sudo add-apt-repository ppa: chris-lea / node.js
sudo apt-get update
sudo apt-get install nodejs
Esto instala el Node.js estable actual en su distribución de Ubuntu, listo para liberar Java‐
Script del navegador y le permite escribir código del lado del servidor.
Si desea compilarlo usted mismo, asumiendo que Git ya está instalado y disponible en
su máquina, escriba lo siguiente:
git clon git: //github.com/joyent/node.git
nodo de cd
git checkout v0.10.7
./configure && make && make install
Consulte http://nodejs.org/ para obtener la última versión de Node.js para verificar
en su sistema.

¡Hola Mundo! Ejemplo


Al abordar un tema nuevo en desarrollo, prefiero comenzar con un ejemplo bastante
con rapidez. Por lo tanto, usaremos el ejemplo probado en batalla en todos los idiomas, "¡Hola, mundo!",
Para
iniciar una conexión a un servidor Node.js compatible con WebSocket y recibir el saludo
sobre la conexión.
Historia de Hello, World!
La encarnación inicial de la primera aplicación de todos en un nuevo idioma / tecnología.
fue escrito por primera vez en 1972 de Brian Kernighan "A Tutorial Introduction to the Language
SI." La aplicación se utilizó para ilustrar variables externas en el idioma.
Comenzará escribiendo código que inicia un servidor compatible con WebSocket en el puerto 8181.
Primero,
usará el modismo CommonJS y requerirá el módulo ws y asignará esa clase a
el objeto WebSocketServer . Entonces llamarás al constructor con tu inicialización
objeto, que consiste en la definición del puerto, o que contiene la definición del puerto.
El protocolo WebSocket es esencialmente una función de paso de mensajes. Para empezar, lo harás
Escuche un evento llamado conexión . Al recibir un evento de conexión , el pro‐
El objeto WebSocket grabado se utilizará para devolver el mensaje "¡Hola, mundo!" saludo.
¡Hola Mundo! Ejemplo | 3
www.it-ebooks.info

Página 18
Para hacer la vida un poco más simple, y como no me apetece reinventar la rueda, la
Se utilizará la biblioteca derful WebSocket llamada ws . La biblioteca de ws puede ocupar muchos
dolor de cabeza al escribir un servidor (o cliente) WebSocket al ofrecer una API simple y limpia
para su aplicación Node.js.
Instálelo usando npm :
npm install ws
Otra opción popular es utilizar el Biblioteca WebSocket-Node .
Todos los ejemplos de este libro supondrán que el código fuente existe en una carpeta indicada
por el nombre del capítulo abreviado, así que cree un directorio llamado ch1 . Ahora crea un nuevo
archivo llamado server.js en el editor de su elección y agregue este código para su aplicación:
var WebSocketServer = require ( 'ws' ). servidor ,
wss = new WebSocketServer ({ puerto : 8181 });
wss . on ( 'conexión' , función ( ws ) {
consola . log ( 'cliente conectado' );
ws . on ( 'mensaje' , función ( mensaje ) {
consola . log ( mensaje );
});
});
Corto y al grano. A continuación, ejecute el servidor para que esté escuchando al cliente de quien se trata
codificar:
node server.js
Cree un archivo para el cliente llamado client.html y colóquelo en el mismo directorio que el
archivo del servidor. Con este sencillo ejemplo, el cliente puede alojarse en cualquier lugar, incluso
ejecutar
del archivo: // protocolo. En capítulos posteriores, usará bibliotecas HTTP y requerirá una
un enfoque más centrado en la web para la gestión de archivos y directorios.
En esta primera pasada, sin embargo, usará una página HTML básica para llamar al servidor WebSocket.
La estructura de la página HTML es un formulario simple, con un campo de texto y un botón para
iniciar el envío. Los dos métodos para enviar su mensaje serán enviando un formulario
(a través de Retorno / Entrar) o haciendo clic en Enviar! botón. Luego, agregará una acción en el
envío de formulario y el evento onclick del botón para llamar al JavaScript sendMessage
función. Una cosa a tener en cuenta es que el código devuelve falso en el envío del formulario, por lo que
la página no se actualiza.
La inicialización de WebSocket es bastante simple; usted inicia una conexión a una Web
Servidor de socket en el puerto 8181 en localhost . A continuación, debido a que la API de WebSocket es
event-
basado (más sobre esto más adelante), define una función para el evento onopen para generar un
mensaje de estado para una conexión exitosa al servidor. La función sendMessage
simplemente tiene que llamar a la función de envío en la variable ws y tomar el valor dentro de la
campo de texto del mensaje .
4 | Capítulo 1: Inicio rápido
www.it-ebooks.info

Página 19
¡Y listo! Tienes tu primer ejemplo de WebSocket.
<! DOCTYPE html>
<html lang = "en" >
<cabeza>
<title> Demostración de WebSocket Echo </title>
<meta charset = "utf-8" >
<meta name = "viewport" content = "width = device-width, initial-scale = 1" >
<link rel = "stylesheet" href = "http://bit.ly/cdn-bootstrap-css" >
<link rel = "stylesheet" href = "http://bit.ly/cdn-bootstrap-theme" >
<script src = "http://bit.ly/cdn-bootstrap-jq" > </script>
<script>
var ws = new WebSocket ( "ws: // localhost: 8181" );
ws . onopen = función ( e ) {
consola . log ( 'Conexión al servidor abierta' );
}
function sendMessage () {
ws . enviar ( $ ( '#mensaje' ). val ());
}
</script>
</head>
<body lang = "en" >
<div class = "vertical-center" >
<div class = "contenedor" >
<p> & nbsp; </p>
<formulario role = "formulario" id = "chat_form" onsubmit = "sendMessage (); return false;" >
<div class = "form-group" >
<input class = "form-control" type = "text" name = "message" id = "message"
placeholder = "Escriba el texto para hacer eco aquí" value = "" autofocus />
</div>
<button type = "button" id = "enviar" class = "btn btn-primary"
onclick = "sendMessage ();" > Enviar! </button>
</form>
</div>
</div>
<script src = "http://bit.ly/cdn-bootstrap-minjs" > </script>
</body>
</html>
A lo largo del libro, utilizará las dos maravillosas bibliotecas que prevalecen en la Web.
para visualización e interacción:
• Bootstrap 3
• jQuery
¡Hola Mundo! Ejemplo | 5
www.it-ebooks.info

Página 20
En ejemplos posteriores, prescindiremos de incluir el script y CSS
etiquetas de estilo a favor de la brevedad. Puede utilizar el HTML anterior como
plantilla para ejemplos futuros, y simplemente elimine el contenido de la
etiqueta <script> personalizada y el contenido entre las etiquetas <body>
manteniendo intacto el JavaScript de Bootstrap.
Con eso, abra la página HTML en su navegador favorito (sugiero Google Chrome
o Mozilla Firefox). Envíe un mensaje y observe cómo aparece en la consola de su servidor
salida.
Si está utilizando Chrome, tiene una excelente facilidad para ver las conexiones WebSocket
desde la primera página. Hagamos esto ahora. En el menú de perros calientes, elija Herramientas →
Herramientas de desarrollo (en Windows: F12, Ctrl-Shift-I; en Mac ⌥-⌘-I).
La Figura 1-1 muestra las herramientas de desarrollo de Google Chrome filtradas para llamadas de
WebSocket.
Echo es la primera aplicación que se escribe en el espacio de las redes.
Figura 1-1. Herramientas para desarrolladores de Chrome: pestaña Red
Seleccione la pestaña Red y actualice el HTML de ejemplo. En la tabla debería ver un
una entrada para el HTML y una entrada para la conexión WebSocket con el estado "101
Cambio de protocolos ". Si lo selecciona, verá los encabezados de solicitud y la respuesta
Encabezados para esta conexión:
OBTENGA ws: // localhost: 8181 / HTTP / 1.1
Pragma: sin caché
Origen: nulo
Anfitrión: localhost: 8181
Sec-WebSocket-Key: qalODNsUoRp + 2K9FJty55Q ==
Usuario-Agente: Mozilla / 5.0 (Macintosh; Intel Mac OS X 10_8_3) ...
6 | Capítulo 1: Inicio rápido
www.it-ebooks.info

Página 21
Actualización: websocket
Sec-WebSocket-Extensions: x-webkit-deflate-frame
Control de caché: sin caché
Conexión: actualización
Sec-WebSocket-Versión: 13
HTTP / 1.1 101 protocolos de conmutación
Conexión: actualización
Sec-WebSocket-Accept: nambQ7W9imtAIYpzsw4hNNuGD58 =
Actualización: websocket
Si está acostumbrado a ver encabezados HTTP, esto no debería ser diferente. Tenemos un
algunos encabezados adicionales aquí, incluido Connection: Upgrade , Sec-Websocket-Key ,
Actualización: websocket , que explicaré con más detalle en el Capítulo 8 . Por ahora, disfruta de tu
primer ejemplo de WebSocket, y prepárese para saber por qué WebSocket debería estar en su
radar para su próximo proyecto.

¿Por qué WebSocket?


La capacidad actual de crear aplicaciones similares a las de un escritorio en el navegador se logra con pri‐
marily usando Comet y Ajax. Para utilizar cualquiera de las soluciones, los desarrolladores han confiado
en
hacks en servidores y clientes para mantener las conexiones abiertas por más tiempo y falsificar
conexión en ejecución.
Si bien estos trucos funcionan técnicamente, crean problemas de asignación de recursos en los servidores.
Con los métodos existentes, la latencia percibida para el usuario final puede ser baja, pero la eficiencia
La eficiencia en el backend deja mucho que desear. El sondeo largo hace innecesario
solicita y mantiene un flujo constante de conexiones de apertura y cierre para su
servidores para tratar. No hay posibilidad de superponer otros protocolos sobre Comet
o Ajax, e incluso si pudiera, la simplicidad simplemente no está ahí.
WebSocket le brinda la capacidad de usar una solicitud HTTP actualizada (el Capítulo 8 cubre
los detalles), y enviar datos de una manera basada en mensajes, similar a UDP y con todos
la confiabilidad de TCP. Esto significa una sola conexión y la capacidad de enviar datos.
ida y vuelta entre el cliente y el servidor con una penalización insignificante en la utilización de recursos
ción. También puede colocar otro protocolo encima de WebSocket y proporcionarlo en un
forma segura sobre TLS. Los capítulos posteriores profundizan en estas y otras características, como
latidos, dominio de origen y más.
Uno de los errores comunes de elegir entre WebSocket y una encuesta larga fue la
triste estado de soporte del navegador. Hoy en día, el estado del soporte del navegador para WebSocket es
mucho más brillante para el usuario final.
La Tabla 1-1 muestra el estado actual de la compatibilidad del navegador con WebSocket. Para los más
up-
información actualizada sobre el soporte de WebSocket, puede consultar el sitio web Can I Use.
¿Por qué WebSocket? | 7
www.it-ebooks.info

Página 22
Tabla 1-1. El estado del soporte del navegador WebSocket
Navegador
Sin soporte
Soporte parcial
Apoyo total
ES DECIR
Versiones 8.0, 9.0
Versión 10.0 y posteriores
Firefox
Versión 27.0 y posteriores
Cromo
Versión 31.0 y posteriores
Safari
Versión 7 y posteriores
Ópera
Versión 20.0 y posteriores
Safari de iOS
Versiones 3.2, 4.0–4.1 Versiones 4.2–4.3, 5.0–5.1 Versión 6.0 y posteriores
mini Opera
Versiones 5.0–7.0
Navegador de Android
Versiones 2.1–4.3
Versión 4.4
Navegador BlackBerry
Versiones 7.0, 10.0
IE móvil
Versión 10.0
Como descubrirás en el Capítulo 5, puede mitigar la falta de soporte en las cejas más antiguas
ers para WebSocket nativo mediante el uso de bibliotecas de marco como SockJS o Socket.IO.

Resumen
Este capítulo presentó WebSocket y cómo construir un servidor de eco simple usando
Node.js. Viste cómo construir un cliente simple para probar tu servidor WebSocket,
junto con una forma sencilla de probar su servidor WebSocket utilizando Chrome Developer
Herramientas. Los siguientes capítulos exploran la API de WebSocket y el protocolo, y
aprenda a superponer otros protocolos sobre WebSocket para darle aún más poder.
8 | Capítulo 1: Inicio rápido
www.it-ebooks.info
Página 23
CAPITULO 2
API de WebSocket
Este capítulo expone los detalles detrás del uso del programa de aplicación WebSocket.
interfaz de ming (API). WebSocket es una comunicación asíncrona dúplex completa impulsada por
eventos
canal de comunicaciones para sus aplicaciones web. Tiene la capacidad de brindarle en tiempo real
actualizaciones que en el pasado usarías encuestas largas u otros trucos para lograr. los
El beneficio principal es reducir las necesidades de recursos tanto del cliente como (más importante)
el servidor.
Si bien WebSocket usa HTTP como mecanismo de transporte inicial, la comunicación
no termina después de que el cliente recibe una respuesta. Con la API de WebSocket,
puede liberarse de las limitaciones del ciclo típico de solicitud / respuesta HTTP. Esta
también significa que mientras la conexión permanezca abierta, el cliente y el servidor pueden
enviar mensajes de forma asíncrona sin sondear nada nuevo.
A lo largo de este capítulo, creará un cliente simple de cotización de acciones utilizando WebSocket
como
transporte de datos y aprenda sobre su API simple en el proceso. Vas a crear un
nueva carpeta de proyecto, ch2 , para almacenar todo su código para este capítulo. Su código de cliente
estar en un archivo llamado client.html y el código de su servidor en un archivo llamado server.js .

Inicializando
El constructor de WebSocket requiere una URL para iniciar una conexión a
el servidor. De forma predeterminada, si no se especifica ningún puerto después del host, se conectará a
través del puerto 80
(el puerto HTTP) o el puerto 443 (el puerto HTTPS).
Si ya está ejecutando un servidor web tradicional en el puerto 80, tendrá que usar un
servidor que comprende y puede utilizar el proxy de la conexión WebSocket, o puede pasar la
conexión a través de su aplicación escrita a medida. El capítulo 5 presenta un pop
opción ular usando nginx para pasar a través de una conexión mejorada a su Node.js-
servidor basado.
9
www.it-ebooks.info

Página 24
Por ahora, debido a que ejecutará el servidor WebSocket localmente, sin una red
servidor proxy de la conexión, simplemente puede inicializar la Web nativa del navegador
Objeto de socket con el siguiente código:
var ws = new WebSocket ( "ws: // localhost: 8181" );
Ahora tiene un objeto WebSocket llamado ws que puede usar para escuchar eventos. los
La sección “Eventos de WebSocket” en la página 12 detalla varios eventos disponibles para escuchar.
La Tabla 2-1 enumera los parámetros del constructor disponibles con WebSocket.
Tabla 2-1. Parámetros del constructor de WebSocket
Nombre del parámetro
Descripción
URL
ws: // o wss: // (si usa TLS)
protocolo (opcional) Parámetro que especifica subprotocolos que pueden usarse como una matriz o una sola cadena
El segundo parámetro opcional en el constructor de WebSocket son los protocolos , pasados en
encabezados como Sec-WebSocket-Protocol . Puede ser una sola cadena de protocolo o una
matriz de cadenas de protocolo. Estos indican subprotocolos, por lo que un solo servidor puede
implementar
ment múltiples subprotocolos de WebSocket. Si no pasa nada, una cadena vacía es
ficticio. Si se suministran subprotocols y el servidor no acepta ninguno de ellos, el
no se establecerá la conexión. En el capítulo 4 , creará un subprotocolo para
STOMP y aprenda a usarlo en WebSocket.
Si hay un intento de iniciar una conexión WebSocket mientras se usa HTTPS en el
sitio web origen, pero utilizando el método de protocolo no TLS de ws: // , un SECURITY_ERR voluntad
ser arrojado. Además, recibirá el mismo error si intenta conectarse a un
Servidor WebSocket a través de un puerto al que el agente de usuario bloquea el acceso (normalmente 80
y
443 siempre están permitidos).
A continuación se muestra una lista de los tipos de protocolo disponibles para usar con WebSocket:
Protocolos registrados
En la especificación para WebSocket RFC 6455, la sección 11.5 define el nombre del subprotocolo
Registro de registros mantenidos por IANA.
Protocolos abiertos
Además, puede utilizar protocolos abiertos que no están registrados, como Extensible
Protocolo de mensajería y presencia (XMPP) o Simple Text Oriented Message Pro‐
tocol (STOMP), y varios otros.
Protocolos personalizados
Puede diseñar cualquier protocolo que desee, siempre que su servidor y cliente
ambos lo apoyan. Se recomienda que utilice nombres que contengan el ASCII
10 | Capítulo 2: API de WebSocket
www.it-ebooks.info

Página 25
versión del nombre de dominio del creador del subprotocolo; por ejemplo,
chat.acme.com .

IU de ejemplo de stock
El ejemplo que creará se basa en datos estáticos para facilitar la vida. Tu servidor tendrá
una lista de símbolos de acciones con valores predefinidos y aleatorizar los cambios de precio en
un espectro de pequeños valores positivos / negativos.
Para mostrar una interfaz de usuario de aspecto más limpio y facilitar el proceso de modificación de CSS,
utilizará Twit‐
Bootstrap de ter y jQuery . Copie y pegue el contenido del siguiente fragmento de código
en su archivo client.html :
<! DOCTYPE html>
<html lang = "en" > <head>
<title> Gráfico de cotizaciones en WebSocket </title>
<meta charset = "utf-8" >
<meta http-equiv = "X-UA-Compatible" content = "IE = edge" >
<meta name = "viewport" content = "width = device-width, initial-scale = 1" >
<link rel = "stylesheet" href = "http://bit.ly/cdn-bootstrap-css" >
<link rel = "stylesheet" href = "http://bit.ly/cdn-bootstrap-theme" >
<script src = "http://bit.ly/cdn-bootstrap-jq" > </script>
<script language = "text / javascript" >
// el código del capítulo va aquí
</script>
</head>
<body lang = "en" >
<div class = "vertical-center" >
<div class = "contenedor" >
<h1> Gráfico de cotizaciones a través de WebSocket </h1>
<table class = "table" id = "stockTable" >
<thead>
<tr>
<th> Símbolo </th>
<th> Precio </th>
</tr>
</thead>
<tbody id = "stockRows" >
<tr>
<td> <h3> AAPL </h3> </td>
<td id = "AAPL" >
<h3> <span class = "label label-default" > 95,00 </span> </h3>
</td>
</tr>
<tr>
<td> <h3> MSFT </h3> </td>
<td id = "MSFT" >
<h3> <span class = "label label-default" > 50,00 </span> </h3>
</td>
IU de ejemplo de stock | 11
www.it-ebooks.info

Página 26
</tr>
<tr>
<td> <h3> AMZN </h3> </td>
<td id = "AMZN" >
<h3> <span class = "label label-default" > 300,00 </span> </h3>
</td>
</tr>
<tr>
<td> <h3> GOOG </h3> </td>
<td id = "GOOG" >
<h3> <span class = "label label-default" > 550,00 </span> </h3>
</td>
</tr>
<tr>
<td> <h3> YHOO </h3> </td>
<td id = "YHOO" >
<h3> <span class = "label label-default" > 35,00 </span> </h3>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script src = "http://bit.ly/maxcdn-bootstrap-js" > </script>
</body> </html>

Eventos de WebSocket
La API para WebSocket se basa en eventos. Esta sección cubre los cuatro eventos
que su código de cotización puede escuchar. Daré descripciones de cada uno, describiré cómo
para manejar situaciones que verá en el campo y construir el ejemplo usando lo que
aprender. Por ejemplo, debe definir algunos bits de datos de muestra para pasar al
servidor:
var stock_request = { "stocks" : [ "AAPL" , "MSFT" , "AMZN" , "GOOG" , "YHOO" ]};
var stocks = { "AAPL" : 0 ,
"MSFT" : 0 ,
"AMZN" : 0 ,
"GOOG" : 0 ,
"YHOO" : 0 };
La Figura 2-1 muestra cómo se ve su aplicación estándar después de conectar el servidor
y cliente.
La primera estructura, stock_request , se pasa después de la conexión exitosa entre
cliente y servidor y le pide al servidor que le siga informando sobre los precios actualizados
en estas poblaciones específicas. La segunda estructura, acciones , es una matriz asociativa simple
12 | Capítulo 2: API de WebSocket
www.it-ebooks.info

Página 27
que mantendrá los valores cambiantes pasados desde el servidor y luego usados para modificar
ify el texto en la tabla y los colores.
Figura 2-1. Gráfico de cotizaciones sobre WebSocket
WebSocket dispara cuatro eventos, que están disponibles en la API de JavaScript y definidos
por el W3C:
• abierto
• mensaje
• error
• cerrar
Con JavaScript, escuchas si estos eventos se activan con el controlador en <event
name> , o el método addEventListener () . Su código proporcionará una devolución de llamada que
se ejecutará cada vez que se active ese evento.
Evento: Abierto
Cuando el servidor WebSocket responde a la solicitud de conexión y el protocolo de enlace
se completa, el evento abierto se activa y se establece la conexión. Una vez que esto sucede,
el servidor ha completado el protocolo de enlace y está listo para enviar y recibir mensajes
desde su aplicación cliente:
Eventos de WebSocket | 13
www.it-ebooks.info

Página 28
// Conexión WebSocket establecida
ws . onopen = función ( e ) {
consola . log ( "Conexión establecida" );
ws . enviar ( JSON . stringify ( stock_request ));
};
Desde dentro de este controlador, puede enviar mensajes al servidor y mostrar el estado
a la pantalla, y la conexión está lista y disponible para la comunicación bidireccional
ción. El mensaje inicial que se envía al servidor a través de WebSocket es el
stock_request estructura como una cadena JSON. Tu servidor ahora sabe lo que te almacena
desea recibir actualizaciones y se las enviará al cliente en intervalos de un segundo.
Evento: Mensaje
Una vez que haya establecido una conexión con el servidor WebSocket, estará disponible para
enviar mensajes a (lo verá en "Métodos WebSocket" en la página 16 ), y
recibir mensajes. La API de WebSocket preparará mensajes completos para ser pro‐
cesado en el controlador de mensajes .
El Capítulo 8 cubre el protocolo WebSocket con más detalle, incluida información sobre
marcos y el flujo de datos entre el servidor y el cliente. Por ahora, el
Lo único que hay que recordar es que cuando el servidor tiene datos, la API de WebSocket llamará
el controlador onmessage :
// función de actualización de UI
var changeStockEntry = function ( symbol , originalValue , newValue ) {
var valElem = $ ( '#' + símbolo + 'intervalo' );
valElem . html ( newValue . toFixed ( 2 ));
if ( newValue < originalValue ) {
valElem . addClass ( 'etiqueta-peligro' );
valElem . removeClass ( 'etiqueta-éxito' );
} else if ( newValue > originalValue ) {
valElem . addClass ( 'etiqueta-éxito' );
valElem . removeClass ( 'etiqueta-peligro' );
}
}
// controlador de mensajes WebSocket
ws . onmessage = function ( e ) {
var stocksData = JSON . analizar ( p . datos );
para ( símbolo var en stocksData ) {
if ( stocksData . hasOwnProperty ( símbolo )) {
changeStockEntry ( símbolo , stocks [ símbolo ], stocksData [ símbolo ]);
stocks [ símbolo ] = stocksData [ símbolo ];
}
}
};
14 | Capítulo 2: API de WebSocket
www.it-ebooks.info

Página 29
Puede ver en este breve fragmento que el controlador está recibiendo un mensaje del
servidor a través de una devolución de llamada onmessage . Al consultar datos, el atributo de datos
contener valores de stock actualizados. El fragmento de código anterior hace lo siguiente:
1. Analiza la respuesta JSON dentro de e.data
2. Itera sobre la matriz asociativa
3. Asegura que la clave exista en la matriz.
4. Llama al fragmento de actualización de la interfaz de usuario
5. Asigna los nuevos valores de stock a su matriz local.
Estás pasando cadenas regulares aquí, pero WebSocket tiene soporte completo para enviar
ing texto y datos binarios.
Evento: error
Cuando ocurre una falla por cualquier motivo, el controlador que ha adjuntado al error
el evento se dispara. Cuando ocurre un error, se puede suponer que la conexión WebSocket
se cerrará y se disparará un evento cercano . Porque el evento cercano ocurre en breve
después de un error en algunos casos, el código y los atributos de motivo pueden darle algunos
indicación de lo sucedido. Aquí hay una muestra de cómo manejar el caso de error y
posiblemente vuelva a conectarse al servidor WebSocket también:
ws . onerror = función ( e ) {
consola . log ( "falla de WebSocket, error" , e );
handleErrors ( e );
};

Evento: PING / PONG


El protocolo WebSocket llama a dos tipos de tramas: PING y PONG. El WebSocket
La API de cliente JavaScript no proporciona la capacidad de enviar un marco PING al servidor. SILBIDO
Los marcos son enviados solo por el servidor, y las implementaciones del navegador deben enviar
back PONG frames en respuesta.
Evento: Cerrar
El evento de cierre se activa cuando se cierra la conexión WebSocket y la devolución de llamada
se ejecutará un error . Puede activar manualmente la llamada al evento onclose ejecutando
cortar el método close () en un objeto WebSocket, que terminará la conexión
ción con el servidor. Una vez cerrada la conexión, la comunicación entre el cliente
y el servidor no continuará. El siguiente ejemplo pone a cero la matriz de existencias al
un evento cercano que se está disparando para mostrar la limpieza de los recursos:
Eventos de WebSocket | 15
www.it-ebooks.info

Página 30
ws . onclose = function ( e ) {
consola . log ( e . motivo + "" + e . código );
para ( símbolo var en acciones ) {
if ( stocks . hasOwnProperty ( símbolo )) {
acciones [ símbolo ] = 0 ;
}
}
}
ws . cerrar ( 1000 , 'Conexión WebSocket cerrada' )
Como se menciona brevemente en "Evento: Error" en la página 15, dos atributos, código y razón ,
son transmitidos por el servidor y podrían indicar una condición de error que debe manejarse
y / o una razón del evento cercano (que no sea la expectativa normal). Cualquiera de los lados puede
terminar la conexión a través del método close () en el objeto WebSocket, como se muestra
en el código anterior. Su código también puede usar el atributo booleano wasClean para encontrar
si la terminación estaba limpia o para ver el resultado de un estado de error.
El valor readyState pasará de cerrar (2) a cerrado (3). Ahora pasemos a
los métodos disponibles para su objeto WebSocket.

Métodos de WebSocket
Los creadores de WebSocket mantuvieron sus métodos bastante simples; solo hay dos:
enviar () y cerrar () .
Método: enviar
Cuando se haya establecido su conexión, estará listo para comenzar a enviar (y
recibir) mensajes hacia / desde el servidor WebSocket. La aplicación cliente puede especificar
qué tipo de datos se están transmitiendo y aceptará varios, incluyendo cadenas y
valores binarios . Como se mostró anteriormente, el código de cliente envía una cadena JSON de listados
cepo:
ws . enviar ( JSON . stringify ( stock_request ));
Por supuesto, realizar este envío en cualquier lugar no será apropiado. Como hemos dicho
Maldición, WebSocket está controlado por eventos, por lo que debe asegurarse de que la conexión esté
abierta
y listo para recibir mensajes. Puede lograr esto de dos formas principales.
Puede realizar su envío desde dentro del evento onopen :
var ws = new WebSocket ( "ws: // localhost: 8181" );
ws . onopen = función ( e ) {
ws . enviar ( JSON . stringify ( stock_request ));
}
16 | Capítulo 2: API de WebSocket
www.it-ebooks.info
Página 31
O puede verificar el atributo readyState para asegurarse de que el objeto WebSocket esté
listo para recibir mensajes:
function processEvent ( e ) {
if ( ws . readyState === WebSocket . OPEN ) {
// ¡Toma abierta, envía!
ws . enviar ( e );
} más {
// Muestra un error, colócalo para enviarlo más tarde, etc.
}
}

Método: Cerrar
Cierra la conexión WebSocket o finaliza un intento de conexión.
a través del método close () . Después de llamar a este método, no se pueden enviar más datos o
recibido de esta conexión. Y llamarlo varias veces no tiene ningún efecto.
Aquí hay un ejemplo de cómo llamar al método close () sin argumentos:
// Cerrar la conexión WebSocket
ws . cerrar ();
Opcionalmente, puede pasar un código numérico y un motivo legible por humanos a través del
método close () . Esto le da alguna indicación al servidor de por qué la conexión
se cerró en el extremo del cliente. El siguiente código muestra cómo pasar esos valores.
Tenga en cuenta que si no pasa un código, se asume el estado 1000, lo que significa
CLOSE_NORMAL :
// Cierre la conexión de WebSocket con motivo.
ws . cerrar ( 1000 , "¡Adiós, mundo!" );
La Tabla 2-2 enumera los códigos de estado que puede usar en el método close () de WebSocket .
Tabla 2-2. Códigos de cierre de WebSocket
Estado
código
Nombre
Descripción
0–999
Reservado y no utilizado.
1000
CLOSE_NORMAL
Cierre normal; la conexión se completó con éxito.
1001
CLOSE_GOING_AWAY
El punto final va a desaparecer, ya sea por una falla del servidor o porque el
el navegador se aleja de la página que abrió la conexión.
1002
CLOSE_PROTOCOL_
ERROR
El punto final está terminando la conexión debido a un error de protocolo.
Métodos WebSocket | 17
www.it-ebooks.info

Página 32
Estado
código
Nombre
Descripción
1003
CLOSE_UNSUPPORTED La conexión se está terminando porque el punto final recibió datos de un tipo que
no se puede aceptar.
1004
CLOSE_TOO_LARGE
El punto final está terminando la conexión porque se recibió una trama de datos
eso es demasiado grande.
1005
CLOSE_NO_STATUS
Reservado. Indica que no se proporcionó ningún código de estado aunque se
esperado.
1006
CLOSE_ABNORMAL
Reservado. Se utiliza para indicar que una conexión se cerró de forma anormal.
1007–1999
Reservado para uso futuro por el estándar WebSocket.
2000–2999
Reservado para su uso por extensiones WebSocket.
3000–3999
Disponible para su uso por bibliotecas y marcos. No puede ser utilizado por aplicaciones.
4000–4999
Disponible para su uso por aplicaciones.

Atributos de WebSocket
Cuando se dispara el evento para abrir , el objeto WebSocket puede tener varios posibles
atributos que se pueden leer en sus aplicaciones cliente. Esta sección presenta
atributos y las mejores prácticas para usarlos en su código de cliente.
Atributo: readyState
El estado de la conexión WebSocket se puede verificar a través de WebSocket de solo lectura
atributo de objeto readyState . El valor de readyState cambiará y es un buen
idea de comprobarlo antes de comprometerse a enviar cualquier dato al servidor.
La Tabla 2-3 muestra los valores que verá reflejados en el atributo readyState .
Tabla 2-3. readyState constantes
Nombre del Atributo
Valor de atributo Descripción
WebSocket.CONNECTING 0
La conexión aún no está abierta.
WebSocket.OPEN
1
La conexión está abierta y lista para comunicarse.
WebSocket.CLOSING
2
La conexión está en proceso de cierre.
WebSocket.CLOSED
3
La conexión está cerrada o no se pudo abrir.
18 | Capítulo 2: API de WebSocket
www.it-ebooks.info

Página 33
Cada uno de estos valores se puede verificar en diferentes puntos para depurar y para
mantener el ciclo de vida de su conexión al servidor.
Atributo: bufferAmount
También se incluye con los atributos la cantidad de datos almacenados en búfer para enviar a
servidor. Si bien esto se usa principalmente al enviar datos binarios, porque el tamaño de los datos
tiende a ser mucho más grande, el navegador se encargará de poner en cola correctamente los datos para
enviar. Debido a que en este punto solo se trata del código del cliente (el siguiente capítulo
trata sobre el protocolo), gran parte del detrás de escena está oculto a su vista.
El uso del atributo bufferAmount puede ser útil para garantizar que se envíen todos los datos
antes de cerrar una conexión o realizar su propia limitación en el lado del cliente.
Atributo: protocolo
Reflexionando sobre el constructor de WebSocket, el argumento de protocolo opcional
le permite enviar uno o varios subprotocolos que el cliente está solicitando. El servidor
decide qué protocolo elige, y esto se refleja en este atributo para la Web‐
Conexión de enchufe. El apretón de manos cuando se complete debe contener una selección de
uno que fue enviado por el cliente, o vacío si no se eligió u ofreció ninguno.

Servidor de ejemplo de stock


Ahora que tiene un cliente en funcionamiento que se conectará a un servidor WebSocket para
recuperar cotizaciones de acciones, es hora de mostrar cómo se ve el servidor:
var WebSocketServer = require ( 'ws' ). servidor ,
wss = new WebSocketServer ({ puerto : 8181 });
var stocks = {
"AAPL" : 95,0 ,
"MSFT" : 50,0 ,
"AMZN" : 300,0 ,
"GOOG" : 550,0 ,
"YHOO" : 35,0
}
function randomInterval ( min , max ) {
devuelve Math . piso ( Matemáticas . aleatorio () * ( máximo - mínimo + 1 ) + mínimo );
}
var stockUpdater ;
var randomStockUpdater = function () {
para ( símbolo var en acciones ) {
if ( stocks . hasOwnProperty ( símbolo )) {
var randomizedChange = randomInterval ( - 150 , 150 );
var floatChange = randomizedChange / 100 ;
stocks [ símbolo ] + = floatChange ;
Servidor de ejemplo de stock | 19
www.it-ebooks.info

Página 34
}
}
var randomMSTime = randomInterval ( 500 , 2500 );
stockUpdater = setTimeout ( function () {
randomStockUpdater ();
}, randomMSTime )
}
randomStockUpdater ();
wss . on ( 'conexión' , función ( ws ) {
var clientStockUpdater ;
var sendStockUpdates = function ( ws ) {
if ( ws . readyState == 1 ) {
var stocksObj = {};
for ( var i = 0 ; i < clientStocks . length ; i ++ ) {
símbolo = existenciascliente [ i ];
stocksObj [ símbolo ] = stocks [ símbolo ];
}
ws . enviar ( JSON . stringify ( stocksObj ));
}
}
clientStockUpdater = setInterval ( function () {
sendStockUpdates ( ws );
}, 1000 );
var clientStocks = [];
ws . on ( 'mensaje' , función ( mensaje ) {
var stock_request = JSON . analizar ( mensaje );
clientStocks = stock_request [ 'existencias' ];
sendStockUpdates ( ws );
});
ws . en ( 'cerrar' , función () {
if ( typeof clientStockUpdater ! == 'undefined' ) {
clearInterval ( clientStockUpdater );
}
});
});
Después de la ejecución, el código del servidor ejecuta una función durante un período de tiempo variable
(entre 0,5 y 2,5 segundos) y actualiza los precios de las acciones. Hace esto para parecer aleatorio
como sea posible en un ejemplo de libro sin requerir código para salir y recuperar existencias reales
precios (vea el Capítulo 4 para eso). Su interfaz espera recibir una lista estática de cinco
acciones recuperadas del servidor. Sencillo. Después de recibir el evento de conexión de
el cliente, el servidor configura una función para que se ejecute cada segundo y devuelve la lista de
cinco acciones con precios aleatorios una vez por segundo. El servidor puede aceptar solicitudes de
20 | Capítulo 2: API de WebSocket
www.it-ebooks.info

Página 35
diferentes acciones siempre que esos símbolos de acciones y un precio inicial se agreguen al
almacena el objeto JavaScript definido en el servidor.

Prueba de compatibilidad con WebSocket


Si ha codificado algo para la Web a lo largo de los años, no debería sorprenderle.
que los navegadores no siempre son compatibles con la última tecnología. Porque algunos
Los navegadores más antiguos no son compatibles con la API de WebSocket, es importante verificar la
compatibilidad
ibilidad antes de usarlo. El capítulo 5 presenta alternativas si los navegadores cliente utilizados por
su comunidad de usuarios no admite la API de WebSocket. Por ahora, aquí hay un
forma de comprobar si la API es compatible con el cliente:
if ( ventana . WebSocket ) {
consola . log ( "WebSocket: compatible" );
// ... código aquí para hacer cosas de WebSocket
} más {
consola . log ( "WebSocket: no admitido" );
// ... modo de respaldo o error de regreso al usuario
}

Resumen
Este capítulo repasó los detalles esenciales de la API de WebSocket y cómo usar cada uno de
ellos dentro de su aplicación cliente. Discutió los eventos, mensajes,
atributos y métodos, y mostró un código de muestra en el camino.
En el capítulo 3, escribirás una aplicación de chat bidireccional y aprenderás a pasar mensajes
sabios de ida y vuelta con múltiples clientes conectados.
Prueba de compatibilidad con WebSocket | 21
www.it-ebooks.info

Página 36
www.it-ebooks.info

Página 37
CAPÍTULO 3
Chat bidireccional
Su primer ejemplo completo es crear un chat bidireccional usando WebSocket. los
El resultado final será un servidor que acepta conexiones y mensajes WebSocket para su
“Sala de chat” y distribuye los mensajes a los clientes conectados. El protocolo WebSocket
en sí es simple, por lo que para escribir su aplicación de chat, administrará la colección de
datos del mensaje en una matriz y mantenga el socket y el UUID único para el cliente en
variables de ámbito local.

Sondeo largo
El sondeo largo es un proceso que mantiene viva una conexión con el servidor sin tener
datos enviados inmediatamente al cliente. Sondeo largo (o una solicitud HTTP de larga duración)
envía una solicitud de servidor que se mantiene abierta hasta que tenga datos, y el cliente los recibirá
y vuelva a abrir una conexión poco después de recibir datos del servidor. Esto, en efecto,
permite una conexión persistente con el servidor para enviar datos de un lado a otro.
En la práctica, existen dos técnicas comunes para lograrlo. En la primera tecnología
nique, XMLHttpRequest se inicia y luego se mantiene abierto, esperando una respuesta de
el servidor. Una vez que se recibe, se realiza otra solicitud al servidor y se mantiene abierta,
esperando más datos. La otra técnica consiste en escribir etiquetas de script personalizadas
apuntando a un dominio diferente (las solicitudes entre dominios no están permitidas con el
primer método). Luego, las solicitudes se manejan de manera similar y se vuelven a abrir en el
moda típica de las encuestas largas.
El sondeo largo es la forma más común de implementar este tipo de aplicación en
la Web hoy. Lo que verá en este capítulo es un método mucho más simple y eficiente.
método de implementación. En los capítulos siguientes abordará la compatibilidad
problema de los navegadores más antiguos que aún no son compatibles con WebSocket.
23
www.it-ebooks.info

Página 38
Escribir una aplicación de chat básica
El capítulo 1 mostró un servidor básico que aceptaba una conexión WebSocket y enviaba cualquier
mensaje recibido de un cliente conectado a la consola. Echemos otro vistazo a
ese código y agregue las funciones necesarias para implementar su chat bidireccional:
var WebSocketServer = require ( 'ws' ). servidor ,
wss = new WebSocketServer ({ puerto : 8181 });
wss . on ( 'conexión' , función ( enchufe ) {
consola . log ( 'cliente conectado' );
zócalo . on ( 'mensaje' , función ( mensaje ) {
consola . log ( mensaje );
});
});
El WebSocketServer proporcionado por el popular módulo ws Node se inicializa y
comienza a escuchar en el puerto 8181. Puede seguir esto escuchando una conexión de cliente
evento y los eventos de mensaje posteriores que siguen. El evento de conexión acepta un
función de devolución de llamada en la que se pasa un objeto de socket que se utilizará para escuchar
mensajes
después de que se haya producido una conexión exitosa. Esto funciona bien para mostrar una simplecon‐
conexión para nuestros propósitos, y ahora va a construir sobre eso mediante el seguimiento de la
clientes que se conectan y enviar esos mensajes a todos los demás clientes conectados.
El protocolo WebSocket no proporciona ninguna de estas funciones de forma predeterminada; el
la responsabilidad de la creación y el seguimiento es suya. En capítulos posteriores, se sumergirá en
bibliotecas como Socket.IO que amplían la funcionalidad de WebSocket y proporcionan una
API más rica y compatibilidad con versiones anteriores de navegadores más antiguos.
La Figura 3-1 muestra cómo se ve la aplicación de chat actualmente.
Sobre la base del código del Capítulo 1 , importe un módulo de nodo para generar un UUID.
Lo primero es lo primero, usará npm para instalar node-uuid :
% npm instalar node-uuid
var uuid = require ( 'node-uuid' );
Se utiliza un UUID para identificar a cada cliente que se ha conectado al servidor y agregarlos
a una colección. Un UUID le permite segmentar mensajes de usuarios específicos, operar en
esos usuarios, y proporcionar datos dirigidos a esos usuarios según sea necesario.
Identificador único universal
Un UUID es un identificador estandarizado que se usa comúnmente en la construcción de sistemas
distribuidos
y se puede asumir que es "prácticamente único". En general, no se encontrará con colli‐
siones, pero no está garantizado. Por lo tanto, debería estar bien usando esto como su identi‐
fier para su sencilla aplicación de chat.
24 | Capítulo 3: Chat bidireccional
www.it-ebooks.info

Página 39
Figura 3-1. Tu primera aplicación de chat WebSocket
A continuación, mejorará la conexión al servidor con identificación y registro:
var clientes = [];
wss . on ( 'conexión' , función ( ws ) {
var client_uuid = UUID . v4 ();
Clientes . push ({ "id" : client_uuid , "ws" : ws });
consola . log ( 'cliente [% s] conectado' , client_uuid );
Asignar el resultado de la función uuid.v4 a la variable client_uuid le permite
para hacer referencia a él más tarde al identificar los envíos de mensajes y cualquier evento cercano . Un
simple
El objeto de metadatos en forma de JSON contiene el UUID del cliente junto con el Web‐
Objeto de enchufe.
Cuando el servidor recibe un mensaje del cliente, itera sobre todas las conexiones conocidas
clientes ted que utilizan la colección de clientes y devuelven un objeto JSON que contiene el
mensaje e id del remitente del mensaje. Puede notar que esto también devuelve el
mensaje al cliente que inició, y esta simplicidad es por diseño. En la interfaz
cliente, no actualiza la lista de mensajes a menos que sea devuelta por el servidor:
ws . on ( 'mensaje' , función ( mensaje ) {
para ( var i = 0 ; i < clientes . longitud ; i ++ ) {
var clientSocket = clientes [ i ]. ws ;
consola . log ( 'cliente [% s]:% s' , clientes [ i ]. id , mensaje );
clientSocket . enviar ( JSON . stringify ({
"id" : client_uuid ,
Escribir una aplicación de chat básica | 25
www.it-ebooks.info
Página 40
"mensaje" : mensaje
}));
}
});
El servidor WebSocket ahora recibe eventos de mensajes de cualquiera de los cli‐
ents. Después de recibir el mensaje, itera a través de los clientes conectados y envía un
Cadena JSON que incluye el identificador único del cliente que envió el mensaje,
y el mensaje en sí. Cada cliente conectado recibirá esta cadena JSON y podrá
enséñele esto al usuario final.
Un servidor debe manejar correctamente los estados de error y seguir funcionando. No tienes
aún definió qué hacer en el caso de un evento de cierre de WebSocket , pero hay algo
falta que debe abordarse en el código de evento del mensaje . La colección de con‐
los clientes conectados deben tener en cuenta la posibilidad de que el cliente se haya ido, y
asegúrese de que antes de enviar un mensaje , todavía haya una conexión WebSocket abierta.
El nuevo código es el siguiente:
ws . on ( 'mensaje' , función ( mensaje ) {
para ( var i = 0 ; i < clientes . longitud ; i ++ ) {
var clientSocket = clientes [ i ]. ws ;
if ( clientSocket . readyState === WebSocket . OPEN ) {
consola . log ( 'cliente [% s]:% s' , clientes [ i ]. id , mensaje );
clientSocket . enviar ( JSON . stringify ({
"id" : client_uuid ,
"mensaje" : mensaje
}));
}
}
});
Ahora tiene un servidor que aceptará conexiones de clientes de WebSocket y
retransmitir los mensajes recibidos a todos los clientes conectados. Lo último que hay que manejar es el
evento cercano :
ws . en ( 'cerrar' , función () {
para ( var i = 0 ; i < clientes . longitud ; i ++ ) {
if ( clientes [ i ]. id == client_uuid ) {
consola . log ( 'cliente [% s] desconectado' , client_uuid );
Clientes . empalme ( i , 1 );
}
}
});
El servidor escucha un evento de cierre y, al recibirlo para este cliente, itera
a través de la colección y elimina al cliente. Combine esto con el cheque del
bandera readyState para su objeto WebSocket y tiene un servidor que funcionará
con su nuevo cliente.
26 | Capítulo 3: Chat bidireccional
www.it-ebooks.info

Página 41
Más adelante en este capítulo, transmitirá el estado de cli‐
entra junto con sus mensajes de chat.

Cliente WebSocket
El cliente de eco simple del Capítulo 1 se puede utilizar como punto de partida para su
aplicación web de chat. Todo el manejo de la conexión funcionará según lo especificado, y deberá
Escuche el evento onmessage que se ignoraba anteriormente:
ws . onmessage = function ( e ) {
var datos = JSON . analizar ( p . datos );
var mensajes = documento . getElementById ( 'mensajes' );
var mensaje = documento . createElement ( "li" );
mensaje . innerHTML = datos . mensaje ;
mensajes . appendChild ( mensaje );
}
El cliente recibe un mensaje del servidor en forma de objeto JSON. Utilizando
La función de análisis incorporada de JavaScript devuelve un objeto que se puede utilizar para extraer el
campo de mensaje. Agreguemos una lista desordenada simple encima del formulario para que los
mensajes puedan ser
agregado usando los métodos DOM que se muestran en la función. Agregue lo siguiente arriba
el elemento de formulario:
<ul id = "mensajes" > </ul>
Los mensajes se agregarán a la lista utilizando el método DOM appendChild , y
mostrado en cada cliente conectado. Hasta ahora solo ha arañado la superficie de la función
nacionalidad que muestra la mensajería fluida proporcionada por el protocolo WebSocket.
En la siguiente sección, implementará un método para identificar clientes mediante un apodo.

Identidad del cliente


La especificación de WebSocket se ha dejado relativamente simplista en términos de implementación
y carece de algunas de las características que se ven en las alternativas. En tu código hasta ahora,
ya hemos recorrido un largo camino para identificar a cada cliente individualmente. Ahora tu
puede agregar identidades de apodo al código del cliente y del servidor:
var nickname = client_uuid . substr ( 0 , 8 );
Clientes . push ({ "id" : client_uuid , "ws" : ws , "nickname" : nickname });
El servidor se modifica para agregar el apodo del campo a un objeto JSON almacenado localmente para
este cliente. Para identificar de forma única a un cliente conectado que no ha identificado un apodo
elección, puede utilizar los primeros ocho caracteres del UUID y asignarlo al
variable de apodo . Todo esto se devolverá a través de una conexión WebSocket abierta.
entre el servidor y todos sus clientes conectados.
Cliente WebSocket | 27
www.it-ebooks.info

Página 42
Utilizará una convención utilizada con clientes de Internet Relay Chat (IRC) y
aceptar / nick new_nick como el comando para cambiar el apodo del cliente del
cadena aleatoria:
if ( mensaje . indexOf ( '/ nick' ) == 0 ) {
var nickname_array = mensaje . dividir ( '' )
if ( nickname_array . length > = 2 ) {
var old_nickname = apodo ;
nickname = nickname_array [ 1 ];
para ( var i = 0 ; i < clientes . longitud ; i ++ ) {
var clientSocket = clientes [ i ]. ws ;
var nickname_message = "Cliente" + antiguo_nickname +
"cambiado a" + apodo ;
clientSocket . enviar ( JSON . stringify ({
"id" : client_uuid ,
"apodo" : apodo ,
"mensaje" : nickname_message
}));
}
}
}
Este código comprueba la existencia del comando / nick seguido de una cadena de
caracteres que representan un apodo. Actualice su variable de apodo y podrá
crear una cadena de notificación para enviar a todos los clientes conectados a través de la configuración
abierta existente
conexión.
Los clientes aún no conocen este nuevo campo, porque el JSON que envió originalmente
incluido solo id y mensaje . Agrega el campo con el siguiente código:
clientSocket . enviar ( JSON . stringify ({
"id" : client_uuid ,
"apodo" : apodo ,
"mensaje" : mensaje
}));
La función appendLog dentro de la interfaz del cliente debe modificarse para admitir
la adición de la variable de apodo :
function appendLog ( apodo , mensaje ) {
var mensajes = documento . getElementById ( 'mensajes' );
var messageElem = documento . createElement ( "li" );
var message_text = "[" + apodo + "] -" + mensaje ;
messageElem . innerHTML = message_text ;
mensajes . appendChild ( messageElem );
}
La Figura 3-2 muestra su aplicación de chat con la adición de identidad.
28 | Capítulo 3: Chat bidireccional
www.it-ebooks.info
Página 43
Figura 3-2. Chat habilitado para identidad
La firma de su nueva función incluye un apodo junto con un mensaje , y puede
preceda cada mensaje ahora con el apodo del cliente. A petición del cliente, puede
ver un apodo antes de los mensajes en lugar de una cadena aleatoria de caracteres antes
cada mensaje.

Eventos y notificaciones
Si estuvieras en medio de una conversación y otra persona apareciera mágicamente en
frente a ti y comencé a hablar, eso sería extraño. Para aliviar esto, puede agregar
notificación de conexión o desconexión y enviarla a todos los conectados
clientela.
Su código tiene varias instancias en las que se ha tomado la molestia de iterar
sobre todos los clientes conectados, verificando el readyState del socket y enviando una
lar Cadena codificada en JSON con valores variables. Por si acaso, extraerás este
en una función genérica, y llámala desde varios lugares en tu código en su lugar:
function wsSend ( tipo , client_uuid , nickname , message ) {
para ( var i = 0 ; i < clientes . longitud ; i ++ ) {
var clientSocket = clientes [ i ]. ws ;
if ( clientSocket . readyState === WebSocket . OPEN ) {
clientSocket . enviar ( JSON . stringify ({
"tipo" : tipo ,
"id" : client_uuid ,
Eventos y notificaciones | 29
www.it-ebooks.info

Página 44
"apodo" : apodo ,
"mensaje" : mensaje
}));
}
}
}
Con esta función genérica, puede enviar notificaciones a todos los clientes conectados, manejar
el estado de la conexión y codificar la cadena como espera el cliente, así:
wss . on ( 'conexión' , función ( ws ) {
...
wsSend ( "mensaje" , client_uuid , apodo , mensaje );
...
});
Enviar mensajes a todos los clientes después de la conexión es ahora simple. Mensajes de conexión,
los mensajes de desconexión y cualquier notificación que necesite ahora se manejan con su
nueva función.

El servidor
Aquí está el código completo para el servidor:
var WebSocket = require ( 'ws' );
var WebSocketServer = WebSocket . servidor ,
wss = new WebSocketServer ({ puerto : 8181 });
var uuid = require ( 'node-uuid' );
var clientes = [];
function wsSend ( tipo , client_uuid , nickname , message ) {
para ( var i = 0 ; i < clientes . longitud ; i ++ ) {
var clientSocket = clientes [ i ]. ws ;
if ( clientSocket . readyState === WebSocket . OPEN ) {
clientSocket . enviar ( JSON . stringify ({
"tipo" : tipo ,
"id" : client_uuid ,
"apodo" : apodo ,
"mensaje" : mensaje
}));
}
}
}
var clientIndex = 1 ;
wss . on ( 'conexión' , función ( ws ) {
var client_uuid = UUID . v4 ();
var nickname = "AnonymousUser" + clientIndex ;
clientIndex + = 1 ;
Clientes . push ({ "id" : client_uuid , "ws" : ws , "nickname" : nickname });
30 | Capítulo 3: Chat bidireccional
www.it-ebooks.info

Página 45
consola . log ( 'cliente [% s] conectado' , client_uuid );
var connect_message = nickname + "se ha conectado" ;
wsSend ( "notificación" , client_uuid , nickname , connect_message );
ws . on ( 'mensaje' , función ( mensaje ) {
if ( mensaje . indexOf ( '/ nick' ) === 0 ) {
var nickname_array = mensaje . dividir ( '' );
if ( nickname_array . length > = 2 ) {
var old_nickname = apodo ;
nickname = nickname_array [ 1 ];
var nickname_message = "Client" + old_nickname + "cambiado a" + nickname ;
wsSend ( "nick_update" , client_uuid , nickname , nickname_message );
}
} más {
wsSend ( "mensaje" , client_uuid , apodo , mensaje );
}
});
var closeSocket = function ( customMessage ) {
para ( var i = 0 ; i < clientes . longitud ; i ++ ) {
if ( clientes [ i ]. id == client_uuid ) {
var desconectar_mensaje ;
if ( customMessage ) {
disconnect_message = customMessage ;
} más {
mensaje_desconexión = apodo + "se ha desconectado" ;
}
wsSend ( "notificación" , client_uuid , apodo , desconectar_mensaje );
Clientes . empalme ( i , 1 );
}
}
}
ws . en ( 'cerrar' , función () {
closeSocket ();
});
proceso . en ( 'SIGINT' , function () {
consola . log ( "Cerrando cosas" );
closeSocket ( 'El servidor se ha desconectado' );
proceso . salir ();
});
});

El cliente
Aquí está el código completo para el cliente:
<! DOCTYPE html>
<html lang = "en" >
<cabeza>
<title> Demostración bidireccional de WebSocket Chat </title>
El cliente | 31
www.it-ebooks.info

Página 46
<meta charset = "utf-8" >
<meta name = "viewport" content = "width = device-width, initial-scale = 1" >
<link rel = "stylesheet" href = "http://bit.ly/cdn-bootstrap-css" >
<link rel = "stylesheet" href = "http://bit.ly/cdn-bootstrap-theme" >
<script src = "http://bit.ly/cdn-bootstrap-jq" > </script>
<script>
var ws = new WebSocket ( "ws: // localhost: 8181" );
var nickname = "" ;
ws . onopen = función ( e ) {
consola . log ( 'Conexión al servidor abierta' );
}
function appendLog ( tipo , apodo , mensaje ) {
var mensajes = documento . getElementById ( 'mensajes' );
var messageElem = documento . createElement ( "li" );
var preface_label ;
if ( escriba === 'notificación' ) {
preface_label = "<span class = \" label label-info \ "> * </span>" ;
} else if ( type == 'nick_update' ) {
preface_label = "<span class = \" label label-warning \ "> * </span>" ;
} más {
preface_label = "<span class = \" label label-success \ ">"
+ apodo + "</span>" ;
}
var message_text = "<h2>" + preface_label + "& nbsp; & nbsp;"
+ mensaje + "</h2>" ;
messageElem . innerHTML = message_text ;
mensajes . appendChild ( messageElem );
}
ws . onmessage = function ( e ) {
var datos = JSON . analizar ( p . datos );
apodo = datos . apodo ;
appendLog ( datos . tipo , datos . apodo , datos . mensaje );
consola . log ( "ID: [% s] =% s" , datos . ID , datos . mensaje );
}
ws . onclose = function ( e ) {
appendLog ( "Conexión cerrada" );
consola . log ( "Conexión cerrada" );
}
function sendMessage () {
var messageField = documento . getElementById ( 'mensaje' );
if ( ws . readyState === WebSocket . OPEN ) {
ws . enviar ( valor de messageField . );
}
messageField . valor = '' ;
messageField . foco ();
}
función desconectar () {
ws . cerrar ();
}
</script>
32 | Capítulo 3: Chat bidireccional
www.it-ebooks.info

Página 47
</head>
<body lang = "en" >
<div class = "vertical-center" >
<div class = "contenedor" >
<ul id = "messages" class = "list-unstyled" >
</ul>
<h />
<formulario role = "formulario" id = "chat_form" onsubmit = "sendMessage (); return false;" >
<div class = "form-group" >
<input class = "form-control" type = "text" id = "message" name = "message"
placeholder = "Escriba el texto para hacer eco aquí" value = "" autofocus />
</div>
<button type = "button" id = "enviar" class = "btn btn-primary"
onclick = "sendMessage ();" > Enviar mensaje </button>
</form>
</div>
</div>
<script src = "http://bit.ly/cdn-bootstrap-minjs" > </script>
</body>
</html>
La Figura 3-3 muestra la aplicación de chat con la adición de notificaciones.
Figura 3-3. Chatbsoc habilitado para notificaciones
El cliente | 33
www.it-ebooks.info

Página 48
Resumen
En este capítulo, creó un cliente y un servidor de chat completos utilizando WebSocket
protocolo. Construyó constantemente una aplicación de chat simplista en algo más sólido
con solo la API de WebSocket como su tecnología preferida. Efectivo y optimizado
experiencias entre aplicaciones internas, chat en vivo y capas de otros protocolos sobre
HTTP son todas las posibilidades que son nativas de WebSocket.
Todo esto es posible con otra tecnología y, como probablemente haya aprendido antes,
hay más de una forma de resolver un problema. Comet y Ajax son probados en batalla
para ofrecer experiencias similares al usuario final como las proporciona WebSocket. Utilizarlos,
sin embargo, está plagado de ineficiencia, latencia, solicitudes innecesarias y con‐
conexiones al servidor. Solo WebSocket elimina esa sobrecarga y le brinda un enchufe
que es full-duplex, bidireccional y listo para el rock 'n' roll.
En el siguiente capítulo, echará un vistazo a un protocolo popular para superponer capas
WebSocket, para proporcionar transporte sin la sobrecarga de HTTP.
34 | Capítulo 3: Chat bidireccional
www.it-ebooks.info

Página 49
CAPÍTULO 4
STOMP sobre WebSocket
En capítulos anteriores, creó aplicaciones simples utilizando la API de WebSocket tanto en
en el lado del servidor y en el cliente. Creó una aplicación de chat multicliente con Web‐
Socket como capa de comunicación. Capítulo 2 discutido brevemente sobre el uso de subprotocolos
con WebSocket. Ahora tomará todo lo aprendido hasta ahora y colocará otro prototipo
col encima de WebSocket.
STOMP, un acrónimo de Simple Text Oriented Messaging Protocol , es un sencillo
Protocolo similar a HTTP para interactuar con cualquier agente de mensajes STOMP. Cualquier pisotón
el cliente puede interactuar con el intermediario de mensajes y ser interoperable entre idiomas
y plataformas.
En este capítulo, creará un cliente y un servidor que se comunicarán mediante el STOMP.
protocolo sobre WebSocket en lugar de TCP. Aprenderá a conectarse a Rab‐
bitMQ usando el complemento Web-Stomp, que usa WebSocket como su cable subyacente
protocolo.
Como en los capítulos anteriores, creará una nueva carpeta de proyecto para los ejemplos del Capítulo 4
con el nombre abreviado ch4 . Los ejemplos de este capítulo utilizan de nuevo un indicador de cotización,
y utilice la mensajería para suscribirse a las actualizaciones de stock. Además, hay dos ejemplos
en este capítulo, cree un subdirectorio llamado proxy . Creará varios archivos para
construya una tabla de trabajo real de precios de acciones impulsada por STOMP sobre WebSocket. aquí
son los archivos que utilizará:
client.html
La base del código de la interfaz; como antes, copie la plantilla utilizada en el Capítulo 1 .
server.js
El proxy de WebSocket que habla con RabbitMQ usando AMQP mientras escucha
STOMP sobre WebSocket.
35
www.it-ebooks.info

Página 50
stomp_helper.js
Una biblioteca de conveniencia que creará para enviar y recibir solicitudes STOMP.
daemon.js
Un demonio que extrae acciones de Yahoo Finance utilizando YQL y extrae y
envía mensajes a RabbitMQ.

Implementación de STOMP
STOMP es un protocolo de texto simple que es similar a la convención HTTP de una
comando case como CONNECT , seguido de una lista de pares clave / valor de encabezado, y luego
contenido opcional, que en el caso de STOMP es nulo. También es posible
y muy recomendable pasar la longitud del contenido como parámetro a cualquier comando,
y el servidor utilizará ese valor en su lugar como la longitud del contenido pasado.
Conectarse
Como viste en el Capítulo 2, la API del navegador nativo para conectarse a un WebSocket
el servidor toma dos parámetros: URL y protocolo . De esos dos parámetros, solo el
Se requiere URL, pero ahora usará la segunda. Si investiga el registro
protocolos conectados en el Registro de nombres de subprotocolo de WebSocket, encontrarás una entrada
para
STOMP 1.0 , que utiliza el identificador v10.stomp . Como veremos en el Capítulo 8, tú
no es necesario que utilicen un subprotocolo registrado con WebSocket. El subprotocolo
necesita ser compatible con el cliente y el servidor. En su cliente, abra un
conexión de la siguiente manera:
var ws ;
var connect = function () {
if ( ! ws || ws . readyState ! == 1 ) {
ws = new WebSocket ( "ws: // localhost: 8181" , "v10.stomp" );
ws . addEventListener ( 'mensaje' , onMessageHandler );
ws . addEventListener ( 'abierto' , onOpenHandler );
ws . addEventListener ( 'cerrar' , onCloseHandler );
}
}
conectar ();
Como en los ejemplos anteriores, abre una conexión a un servidor WebSocket en el puerto
8181. Pero además, pasa un segundo parámetro en el constructor, que puede
ser una cadena o una matriz de cadenas que identifican los subprotocolos solicitados de la
servidor. Observe también que una función de conexión agrega los detectores de eventos
para abrir , mensaje ,
y cierre utilizando el método addEventListener . Este es el método esencial de
conectando. Si necesita volver a conectarse después de una conexión perdida, los controladores de
eventos
no se vuelve a conectar automáticamente si está utilizando el método ws.on <eventname> .
36 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info

Página 51
Después de abrir la conexión WebSocket, se dispara un evento abierto y puede desactivar
enviar y recibir mensajes directamente desde el servidor. Si hace referencia a STOMP 1.0
documento de protocolo , lo siguiente se mostrará como el método de conexión inicial a un
Servidor compatible con STOMP:
CONECTAR
inicio de sesión: <nombre de usuario>
contraseña: <contraseña>
^@
Para nuestro ejemplo, usará websockets como nombre de usuario y rabbitmq como contraseña.
palabra para toda la autenticación con el servidor STOMP y RabbitMQ. Entonces dentro de tu
código, pase lo siguiente con la función de envío de WebSocket :
var frame = "CONECTAR \ n"
+ "inicio de sesión: websockets \ n" ;
+ "contraseña: rabbitmq \ n" ;
+ "apodo: anónimo \ n" ;
+ "\ n \ n \ 0" ;
ws . enviar ( marco );
Puede ver en el documento del protocolo STOMP 1.0 que cada marco enviado termina con el nulo
terminator ^ @ , o si se pasa el encabezado de longitud de contenido , se usará en su lugar.
Debido a la simplicidad de WebSocket, está mapeando cuidadosamente los marcos STOMP en
parte superior de los marcos de WebSocket en estos ejemplos. Si el servidor acepta la conexión y
información de autenticación, devuelve lo siguiente al cliente, que incluye
un ID de sesión que se utilizará en llamadas posteriores al servidor:
CONECTADO
sesión: <session-id>
^@
La introducción del capítulo menciona stomp_helper.js, y antes de llegar al
código del servidor, revisemos la biblioteca que ayudará a enviar y recibir STOMP-
marcos compatiblesEjemplo 4-1 ).
Ejemplo 4-1. Código de biblioteca STOMP
( función ( exportaciones ) {
exportaciones . process_frame = function ( data ) {
var líneas = datos . dividir ( "\ n" );
var frame = {};
marco [ 'encabezados' ] = {};
if ( líneas . longitud > 1 ) {
marco [ 'comando' ] = líneas [ 0 ];
var x = 1 ;
while ( líneas [ x ]. longitud > 0 ) {
var header_split = líneas [ x ]. dividir ( ':' );
Implementación de STOMP | 37
www.it-ebooks.info

Página 52
var key = header_split [ 0 ]. recortar ();
var val = header_split [ 1 ]. recortar ();
marco [ 'encabezados' ] [ clave ] = val ;
x+=1;
}
marco [ 'contenido' ] = líneas
. empalme ( x + 1 , líneas . longitud - x )
. unirse ( "\ n" );
marco [ 'contenido' ] = marco [ 'contenido' ]
. subcadena ( 0 , marco [ 'contenido' ]. longitud - 1 );
}
marco de retorno ;
};
exportaciones . send_frame = function ( ws , frame ) {
var data = frame [ 'comando' ] + "\ n" ;
var header_content = "" ;
para ( clave var en el marco [ 'encabezados' ]) {
if ( marco [ 'encabezados' ]. hasOwnProperty ( clave )) {
header_content + = clave
+ ":"
+ marco [ 'encabezados' ] [ clave ]
+ "\ n" ;
}
}
datos + = header_content ;
datos + = "\ n \ n" ;
datos + = marco [ 'contenido' ];
datos + = "\ n \ 0" ;
ws . enviar ( datos );
};
exportaciones . send_error = function ( ws , message , detail ) {
encabezados = {};
if ( mensaje ) encabezados [ 'mensaje' ] = mensaje ;
else headers [ 'message' ] = "No se ha proporcionado ningún mensaje de error" ;
exportaciones . send_frame ( ws , {
"comando" : "ERROR" ,
"encabezados" : encabezados ,
"contenido" : detalle
});
};
}) ( Typeof exportaciones === 'indefinido' ? Esta [ 'Stomp' ] = {} : exportaciones );
Los elementos ceremoniales que preceden y siguen a las funciones de esta biblioteca permiten
esto para ser utilizado dentro del navegador, y en el lado del servidor con Node.js en un require
declaración.
38 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info

Página 53
La primera función para describir es process_frame , que toma un marco STOMP como un
parámetro llamado datos y crea un objeto JavaScript que contiene todo lo analizado
para su uso dentro de su aplicación. Como se describe en la Tabla 4-1, divide el
comando, todos los encabezados y cualquier contenido dentro del marco y devuelve un objeto
completamente analizado.
Tabla 4-1. Estructura de objetos JavaScript
Llave
Descripción
comando STOMP comando pasado por el marco
encabezados Un objeto JavaScript con clave / valores para los encabezados pasados
contenido Cualquier contenido enviado en el marco que fue terminado en nulo o se adhiere al encabezado
de longitud del contenido
El siguiente e igualmente importante es la función send_frame , que acepta un Web‐
Socket objeto y un marco STOMP en forma de un objeto JavaScript exactamente como usted
enviar desde la función process_frame . La función send_frame toma cada uno de
los valores pasados, crea un marco STOMP válido y lo envía a través del
Parámetro WebSocket .
La función restante es send_error , que toma los parámetros que se muestran en
Tabla 4-2 .
Tabla 4-2. Parámetros aceptados para la llamada send_error
Nombre
Descripción
WebSocket La conexión WebSocket activa
mensaje
Mensaje de error que explica qué salió mal
detalle
Mensaje de detalle opcional pasado en el cuerpo
Podrá utilizar el conjunto de funciones antes mencionado para enviar y recibir STOMP
marcos sin ningún tipo de análisis de cadena dentro de su cliente o código de servidor.
Conexión a través del servidor
En el lado del servidor, al recibir un evento de conexión , su tarea inicial para obtener
nected es analizar lo que se recibe en el marco del mensaje (usando stomp_helper.js
biblioteca), y devuelva un comando CONECTADO o un ERROR si falla:
wss . on ( 'conexión' , función ( ws ) {
var id_sesión = uuid . v4 ();
Implementación de STOMP | 39
www.it-ebooks.info

Página 54
ws . on ( 'mensaje' , función ( mensaje ) {
var frame = Stomp . process_frame ( mensaje );
var encabezados = marco [ 'encabezados' ];
cambiar ( marco [ 'comando' ]) {
caso "CONECTAR" :
Stomp . send_frame ( ws , {
comando : "CONECTADO" ,
encabezados : {
session : sessionid ,
},
contenido : ""
});
romper ;
por defecto :
Stomp . send_error ( ws , "No hay marco de comando válido" );
romper ;
}
});
...
});
Como ha visto en ejemplos anteriores, el evento de conexión se recibe y funciona
comienza. Existe una capa extra gracias a STOMP, que es manejada un poco por
tu biblioteca. Después de asignar un ID de sesión a un UUID y al recibir un mensaje
evento del cliente, lo ejecuta a través de la función process_frame para obtener un Java‐
Objeto de secuencia de comandos que representa el marco recibido. Para procesar cualquier comando
enviado, el programa usa una declaración de caso , y al recibir el comando CONNECT ,
devuelve un marco STOMP para que el cliente sepa que se recibió la conexión
y se acepta junto con el id . de sesión para esta sesión.
Eche un vistazo rápido a la Figura 4-1, que muestra un evento de conexión completado .
Mirando la captura de pantalla, verá un nuevo encabezado para la solicitud HTTP y
respuesta: Sec-WebSocket-Protocol . En el Capítulo 8 puede leer una descripción más detallada
debate sobre los distintos encabezados y profundiza en el meollo del protocolo. aquí
en el ejemplo de acciones, la solicitud enviada incluye el subprotocolo v10.stomp . Si
el servidor acepta este subprotocolo, a su vez, responderá con ese subprotocolo
nombre, y el cliente puede continuar enviando y recibiendo tramas al servidor. Si el
el servidor no habla v10.stomp , recibirá un error.
40 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info

Página 55
Figura 4-1. Conexión exitosa de WebSocket con subprotocolo
La implementación predeterminada de la biblioteca ws aceptará cualquier subprotocolo que sea
enviado. Escribamos un código adicional para asegurarnos de que solo el protocolo v10.stomp
se acepta aquí. Para hacer esto, escribirá un controlador especial al inicializar la Web
Objeto SocketServer :
var WebSocketServer = require ( 'ws' ). servidor ,
wss = new WebSocketServer ({ puerto : 8181 ,
handleProtocols : function ( protocol , cb ) {
var v10_stomp = protocolo [ protocolo . indexOf ( "v10.stomp" )];
if ( v10_stomp ) {
cb ( verdadero , v10_stomp );
volver ;
}
cb ( falso );
}});
En el Capítulo 2, la descripción general de la API de WebSocket mostró que podía pasar más
de un subprotocolo. En su código de controlador, tendrá que descomprimir una matriz de sub‐
protocolos que incluye el que busca el cliente. Como estás usando Node.js,
puede usar convenciones como Array.indexOf sin preocuparse por cosas como Internet
Explorer no lo admite. Con el código anterior, ha realizado correctamente una
apretón de manos aceptando un nuevo subprotocolo.
Como se señaló anteriormente, su primer ejemplo de implementación de STOMP será la aplicación de
acciones.
Enviará solicitudes a través de STOMP desde el cliente al servidor, y el servidor
enviar y recibir mensajes con RabbitMQ mientras el demonio de existencias escupe inter‐
Implementación de STOMP | 41
www.it-ebooks.info

Página 56
actualizaciones importantes de precios. Para comenzar, instale un servidor RabbitMQ para hacer cola
sus mensajes para el servidor.

Configuración de RabbitMQ
Necesitará ejecutar un nodo RabbitMQ para su servidor WebSocket para
solicitudes a. Para hacer eso, necesitará tener Vagrant configurado en su desarrollo
máquina. Vagrant es una herramienta útil para crear desarrollos portátiles y livianos
maquinas virtuales. Instalarlo es tan fácil como tomar el binario de instalación adecuado para su
sistema operativo en el página de descarga de Vagrant.
Vagrant es una herramienta ligera para crear y configurar reproducible
y entornos de desarrollo portátiles. Utiliza VirtualBox o
VMWare bajo el capó para las instancias virtualizadas y permite
para varios proveedores, incluidos Puppet, Chef, Ansible e incluso
scripts de shell simples.
Una vez que haya instalado Vagrant correctamente, cree un nuevo archivo en la carpeta de su proyecto
llamado Vagrantfile e incluyen lo siguiente:
Vagabundo . configurar ( "2" ) hacer | config |
config . vm . hostname = "websockets-mq"
config . vm . caja = "precisa64"
config . vm . box_url = "http://bit.ly/ubuntu-vagrant-precise-box-amd64"
config . vm . red : puerto_enviado , invitado : 5672 , host : 5672
config . vm . la red : forwarded_port , invitado : 15672 , anfitrión : 15672
config . vm . aprovisionamiento "shell" , ruta : "setup_rabbitmq.sh"
config . vm . proveedor : virtualbox do | v |
v . nombre = "websockets-mq"
final
final
El archivo de configuración se usará para crear una nueva instancia de Vagrant usando la imagen en
config.vm.box_url . Reenvía los puertos 5672 y 15672 a la máquina local, y
especifica un aprovisionamiento basado en shell que se ejecutará en vagrant up , que se incluye en
el siguiente código:
#! / bin / bash
cat >> /etc/apt/sources.list << EOT
deb http://www.rabbitmq.com/debian/ testing main
EOT
wget http://www.rabbitmq.com/rabbitmq-signing-key-public.asc
apt-key agregar conejomq-signing-key-public.asc
42 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info

Página 57
apt-get update
apt-get install -q -y pantalla htop vim curl wget
apt-get install -q -y rabbitmq-server
# Complementos RabbitMQ
servicio rabbitmq-server stop
rabbitmq-plugins habilitan rabbitmq_management
servicio rabbitmq-server start
# Crea nuestro usuario de websockets y elimina invitado
rabbitmqctl delete_user invitado
rabbitmqctl add_user websockets rabbitmq
rabbitmqctl set_user_tags administrador de websockets
rabbitmqctl set_permissions -p / websockets ". *" ". *" ". *"
lista de complementos de rabbitmq
El script de aprovisionamiento de shell hace lo siguiente:
• Agrega una nueva fuente para la última instalación de RabbitMQ
• Instala algunas dependencias junto con el servidor RabbitMQ
• Habilita el complemento rabbitmq_management
• Elimina el usuario invitado y crea su nuevo usuario predeterminado rabbitmq: websockets
• Otorga privilegios de administrador a ese usuario
Ahora, desde la línea de comando, inicialice y aprovisione la nueva instancia de Vagrant con
el seguimiento:
vagabundo
Este comando lee el Vagrantfile y ejecuta el script de aprovisionamiento para instalar el
Servidor RabbitMQ en una instancia amd64 de Ubuntu 12.04 para usar en los ejemplos. los
El siguiente código muestra una impresión similar a la que debería ver después de completar
El comando. Inmediatamente después de esta salida, Vagrant ejecutará el shell de aprovisionamiento
script que configura RabbitMQ:
Trayendo la máquina 'predeterminada' con el proveedor 'virtualbox' ...
== > predeterminado: Importando caja base 'precisa64' ...
== > predeterminado: dirección MAC coincidente para redes NAT ...
== > predeterminado: configuración del nombre de la máquina virtual: websockets-mq
== > predeterminado: Borrando cualquier puerto reenviado previamente configurado ...
== > predeterminado: Borrar cualquier interfaz de red configurada previamente ...
== > predeterminado: Preparando interfaces de red según la configuración ...
predeterminado: Adaptador 1: nat
== > predeterminado: Reenvío de puertos ...
predeterminado: 5672 = > 5672 ( adaptador 1 )
predeterminado: 15672 = > 15672 ( adaptador 1 )
Configuración de RabbitMQ | 43
www.it-ebooks.info

Página 58
predeterminado: 22 = > 2222 ( adaptador 1 )
== > predeterminado: Arrancando VM ...
== > predeterminado: Esperando que la máquina arranque. Esto puede tomar unos pocos minutos...
predeterminado: dirección SSH: 127.0.0.1:2222
predeterminado: SSH nombre de usuario: vagabundo
predeterminado: método de autenticación SSH: clave privada
El Vagrantfile incluido , que proporciona la configuración para Vagrant, abre el
siguientes puertos:
tcp / 5672
El puerto predeterminado para amqp
tcp / 15672
La interfaz de gestión web

Conexión del servidor a RabbitMQ


Una vez instaladas las dependencias adecuadas, es hora de retroceder y obtener el
servidor hablando con RabbitMQ. La conexión a RabbitMQ puede ocurrir de forma independiente
del trabajo de WebSocket. Tras la ejecución del servidor, abrirá una conexión a
RabbitMQ y realiza dos acciones con la conexión:
• Escuche la cola de stocks.result para obtener actualizaciones sobre precios
• Publicar solicitudes de stock en un intervalo establecido en la cola de stock.
Para hacer eso con su servidor, necesitará hablar AMQP con RabbitMQ. Existen
muchas bibliotecas para Node.js para hablar AMQP, y la más simple que he encontrado es
node-amqp . Use el comando npm para instalar la biblioteca en la carpeta de su proyecto:
npm instalar amqp
Sus acciones iniciales se basarán en una solicitud CONNECT válida iniciada por el cliente para
el servidor. Creará una conexión a la instancia de RabbitMQ en ejecución, utilizando el
información de autenticación transmitida por el cliente.
Así es como se conectará a la instancia de RabbitMQ que instaló:
amqp = require ( 'amqp' );
var conexión = amqp . createConnection (
{ host : 'localhost' ,
inicio de sesión : 'websockets' ,
contraseña : 'rabbitmq'
});
La biblioteca en uso (amqp) activa eventos que se pueden escuchar para usar devoluciones de llamada. En
el siguiente fragmento, escucha el evento listo y ejecuta la función de devolución de llamada
previsto. Al asegurarse de que la conexión está lista, comienza a escuchar el
44 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info

Página 59
stocks.result cola y suscríbase para recibir actualizaciones de los mensajes que se pasan
volver a través de él. Estos mensajes contendrán precios actualizados para las acciones que tienen
sido solicitado. Notarás que dentro de los bloques, la biblioteca stomp_helper.js es
que se utiliza para enviar marcos de MENSAJE a los clientes que han solicitado actualizaciones sobre
acciones particulares:
conexión . on ( 'listo' , función () {
conexión . queue ( 'stocks.result' , { autoDelete : false , durable : true },
función ( q ) {
q . suscribirse ( función ( mensaje ) {
var datos ;
prueba {
datos = JSON . analizar ( mensaje . datos . toString ( 'utf8' ));
} atrapar ( err ) {
consola . log ( err );
}
para ( var i = 0 ; i < datos . longitud ; i ++ ) {
para ( cliente var en existencias ) {
if ( stocks . hasOwnProperty ( cliente )) {
var ws = stocks [ cliente ]. ws ;
para ( símbolo var en acciones [ cliente ]) {
if ( acciones [ cliente ]. hasOwnProperty ( símbolo )
&& símbolo === datos [ i ] [ 'símbolo' ]) {
existencias [ cliente ] [ símbolo ] = datos [ i ] [ 'precio' ];
var price = parseFloat ( acciones [ cliente ] [ símbolo ]);
Stomp . send_frame ( ws , {
"comando" : "MENSAJE" ,
"encabezados" : {
"destino" : "/ cola / existencias". + símbolo
},
contenido : JSON . stringify ({ price : price })
});
}
}
}
}
}
});
});
});
Conexión del servidor a RabbitMQ | 45
www.it-ebooks.info

Página 60
La carga útil que se recibe de la cola de mensajes de stocks.result se parece a la
siguiendo:
[
{
"símbolo" : "AAPL" ,
"precio" : 149,34
},
{
"símbolo" : "GOOG" ,
"precio" : 593.2600000000037
}
]
Después de analizar la carga útil, el bloque de código itera sobre el resultado y sobre un maestro
lista de existencias almacenadas en todos los clientes conectados. En el proceso de iterar sobre
un objeto JavaScript, debe comprobar para asegurarse de que el valor que se pasa durante
la iteración es parte del objeto mediante myObject.hasOwnProperty (myIterator
Valor) . Mapea el precio actualizado con el precio que se almacena y envía un mensaje
volver al cliente conectado usando STOMP sobre ese destino específico .
Cuando el cliente solicita una nueva acción, se agrega a la lista maestra de
cepo. Un bloque de código separado se ejecuta a intervalos para enviar la lista maestra a un
stocks.work queue, que el daemon.js recoge para encontrar el precio actualizado
y enviarlo de vuelta a la cola stocks.result . Una de las principales razones por las que lo haces
esto es que es más fácil de escalar y el sistema puede procesar más solicitudes si es necesario por
agregando más demonios, sin ningún efecto adverso. El siguiente código muestra el
método de actualización . Crea una matriz de cadenas de símbolos bursátiles y la publica en el
existencias cola de trabajo:
var actualizador = setInterval ( function () {
var st = [];
para ( cliente var en existencias ) {
para ( símbolo var en acciones [ cliente ]) {
if ( símbolo ! == 'ws' ) {
st . empujar ( símbolo );
}
}
}
if ( st . longitud > 0 ) {
conexión . publicar ( 'stocks.work' ,
JSON . stringify ({ "stocks" : st }),
{ deliveryMode : 2 });
}
}, 10000 );
46 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info

Página 61
El demonio del precio de las acciones
El siguiente código es para el demonio, que incluye una serie de símbolos de acciones,
y escupe un objeto JSON con los valores actualizados usando Yahoo YQL. Crear un
nuevo archivo llamado daemon.js e inserte el siguiente fragmento:
#! / usr / bin / env nodo
var request = require ( 'solicitud' ),
amqp = require ( 'amqp' );
módulo . exportaciones = Existencias ;
función Acciones () {
var self = esto ;
}
Existencias . prototipo . lookupByArray = function ( stocks , cb ) {
var csv_stocks = '"' + existencias . join ( '", "' ) + '"' ;
var env_url = '& env = http% 3A% 2F% 2Fdatatables.org% 2Falltables.env & format = json' ;
var url = 'https://query.yahooapis.com/v1/public/yql' ;
var datos = encodeURIComponent (
'seleccione * de yahoo.finance.quotes donde el símbolo en ('
+ csv_stocks + ')' );
var data_url = url
+ '? q ='
+ datos
+ env_url ;
solicitud . get ({ url : data_url , json : true },
función ( error , respuesta , cuerpo ) {
var stocksResult = [];
if ( ! error && response . statusCode == 200 ) {
var totalReturned = cuerpo . consulta . contar ;
para ( var i = 0 ; i < totalReturned ; ++ i ) {
var stock = cuerpo . consulta . resultados . cita [ i ];
var stockReturn = {
'símbolo' : stock . símbolo ,
'precio' : stock . Pedir
};
stocksResult . empujar ( stockReturn );
}
cb ( stocksResult );
} más {
consola . log ( error );
}
});
};
Conexión del servidor a RabbitMQ | 47
www.it-ebooks.info

Página 62
var main = function () {
var conexión = amqp . createConnection ({
host : 'localhost' ,
inicio de sesión : 'websockets' ,
contraseña : 'rabbitmq'
});
var stocks = nuevos stocks ();
conexión . on ( 'listo' , función () {
conexión . queue ( 'stocks.work' , { autoDelete : false , durable : true },
función ( q ) {
q . suscribirse ( función ( mensaje ) {
var json_data = mensaje . datos . toString ( 'utf8' );
var datos ;
consola . log ( json_data );
prueba {
datos = JSON . analizar ( json_data );
} atrapar ( err ) {
consola . log ( err );
}
existencias . lookupByArray ( data . stocks , function ( stocks_ret ) {
var data_str = JSON . stringify ( stocks_ret );
conexión . publicar ( 'stocks.result' , data_str ,
{ deliveryMode : 2 });
});
});
});
});
};
if ( requiere . main === módulo ) {
principal ();
}
Este demonio se puede ejecutar usando node daemon.js y se conectará a RabbitMQ
y procesar el trabajo que extrae de la cola de mensajes de RabbitMQ. Varias convenciones
Las modificaciones deben notarse en el servidor WebSocket STOMP, incluido el método
de conexión y procesando el evento listo . El demonio escuchará
stocks.work cola, sin embargo, para obtener una lista de acciones para buscar, y al final empujar
el resultado de nuevo en la cola stocks.result . Si echa un vistazo a Stocks.proto
type.lookupByArray , está emitiendo una llamada de Yahoo YQL para las acciones solicitadas
y devolver la carga útil JSON, como se vio anteriormente.
48 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info

Página 63
Procesamiento de solicitudes de STOMP
Antes de sumergirse en la interacción del servidor con RabbitMQ, vio cómo lograr
Logre la conexión con STOMP a través de WebSocket utilizando su biblioteca. Continuemos
y desarrolle el resto de los comandos necesarios para interactuar con la interfaz:
wss . on ( 'conexión' , función ( ws ) {
var id_sesión = uuid . v4 ();
existencias [ sessionid ] = {};
sesiones_conectadas . empujar ( ws );
existencias [ sessionid ] [ 'ws' ] = ws ;
ws . on ( 'mensaje' , función ( mensaje ) {
var frame = Stomp . process_frame ( mensaje );
var encabezados = marco [ 'encabezados' ];
cambiar ( marco [ 'comando' ]) {
caso "CONECTAR" :
Stomp . send_frame ( ws , {
comando : "CONECTADO" ,
encabezados : {
sesión : sessionid
},
contenido : ""
});
romper ;
caso "SUBSCRIBE" :
var subscribeSymbol = symbolFromDestination (
marco [ 'encabezados' ] [ 'destino' ]);
acciones [ sessionid ] [ subscribeSymbol ] = 0 ;
romper ;
caso "UNSUBSCRIBE" :
var unsubscribeSymbol = symbolFromDestination (
marco [ 'encabezados' ] [ 'destino' ]);
eliminar acciones [ sessionid ] [ unsubscribeSymbol ];
romper ;
caso "DESCONECTAR" :
consola . log ( "Desconectando" );
closeSocket ();
romper ;
por defecto :
Stomp . send_error ( ws , "No hay marco de comando válido" );
romper ;
}
});
var symbolFromDestination = function ( destino ) {
destino de regreso . substring ( destino . indexOf ( '.' ) + 1 ,
destino . longitud );
};
var closeSocket = function () {
Procesamiento de solicitudes STOMP | 49
www.it-ebooks.info

Página 64
ws . cerrar ();
if ( acciones [ sessionid ] && acciones [ sessionid ] [ 'ws' ]) {
existencias [ sessionid ] [ 'ws' ] = null ;
}
eliminar existencias [ sessionid ];
};
ws . en ( 'cerrar' , función () {
closeSocket ();
});
proceso . en ( 'SIGINT' , function () {
consola . log ( "Cierre por pausa" );
closeSocket ();
proceso . salir ();
});
Como en los ejemplos anteriores, tras una conexión exitosa se genera un UUID que
actuará como su ID de sesión para pasar de un lado a otro en el marco STOMP. los
frame se analizará y colocará en el objeto JavaScript. A partir de ahí realizas dif‐
diferentes acciones basadas en el comando frame pasado. Ya has visto el código de
CONECTAR , por lo que nos centraremos
en SUSCRIBIRSE , ANULAR SUSCRIPCIÓN y DESCONECTARSE .
Tanto la suscripción como la cancelación de la suscripción modifican su objeto de acciones . Al
suscribirte,
está agregando un nuevo símbolo a la lista existente de acciones para ese ID de sesión . Unsub‐
la escritura se cumple simplemente eliminando ese símbolo de la lista para que no se devuelva
al cliente. Recibir un comando DESCONECTAR del cliente se cumple con el cierre
la WebSocket y limpiar cualquier referencia a que el cliente y en las poblaciones de
objeto. Debido a que esta es una aplicación que se ejecutará desde la consola, existe la posibilidad de
recibir
presionar Ctrl-C, lo que rompería la conexión. Para manejar esto, conéctese al SIGINT
evento que se dispara, por lo que puede cerrar el socket con gracia y en sus propios términos.

Cliente
El cliente es una interfaz simple con acciones que varían en precio en función de los datos devueltos
desde el servidor. El formulario en la parte superior toma un símbolo de acciones como entrada e intenta
SUSCRÍBETE a través de STOMP para recibir actualizaciones del servidor. Mientras que la solicitud de
suscripción
se está enviando, se agrega una fila de tabla para el nuevo símbolo, así como un marcador de posición de
“Recuperando…” mientras espera que regresen los datos.
La figura 4-2 muestra un ejemplo práctico de la aplicación de cotización.
50 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info
Página 65
Figura 4-2. Ejemplo de acciones de STOMP sobre WebSocket
El marcado del ejemplo se muestra en el siguiente código. Describe una forma simple
que llama al método subscribe (que se describe a continuación), y la tabla que contiene
los símbolos de acciones, el precio actualizado del servicio y un botón Eliminar. En
Además, se ha agregado un indicador de estado de conexión al servidor WebSocket:
<div class = "vertical-center" >
<div class = "contenedor" >
<div class = "bien" >
<formulario role = "formulario" class = "formulario-en línea" id = "add_form"
onsubmit = "subscribe ($ ('# símbolo'). val ()); return false;" >
<div class = "form-group" >
<input class = "form-control" type = "text" id = "symbol"
nombre = "símbolo" marcador de posición = "Símbolo de acciones: es decir, AAPL" valor = ""
enfoque automático />
</div>
<button type = "submit" class = "btn btn-primary" > Agregar </button>
</form>
</div>
<table class = "table" id = "stockTable" >
<thead>
Cliente | 51
www.it-ebooks.info

Página 66
<tr>
<th> Símbolo </th>
<th> Precio </th>
<th> Acciones </th>
</tr>
</thead>
<tbody id = "stockRows" >
<tr id = "norows" >
<td colspan = "3" >
No se encontraron acciones, agregue una arriba
</td>
</tr>
</tbody>
</table>
<div class = "text-right" >
<p>
<a id = "conexión" class = "btn btn-danger"
href = "#" onclick = "conectar ();" > Sin conexión </a>
</p>
</div>
</div>
</div>
Varias funciones componen su aplicación cliente y se describirán por separado en
el orden en que se ejecutan. La primera función es subscribe , que agrega un nuevo símbolo
bol a la interfaz y lo comunica al servidor:
var subscribe = function ( símbolo ) {
if ( stocks . hasOwnProperty ( símbolo )) {
alerta ( 'Ya agregó el' + símbolo + 'símbolo' );
volver ;
}
acciones [ símbolo ] = 0,0 ;
Stomp . send_frame ( ws , {
"comando" : "SUBSCRIBE" ,
"encabezados" : {
"destino" : "/ cola / existencias". + símbolo ,
},
contenido : ""
});
var tbody = documento . getElementById ( 'stockRows' );
var newRow = tbody . insertRow ( tbody . filas . longitud );
newRow . id = símbolo + '_row' ;
newRow . innerHTML = '<td> <h3>' + símbolo + '</h3> </td>' +
'<td id = "' + símbolo + '">' +
'<h3>' +
'<span class = "label label-default"> Recuperando .. </span>' +
'</h3>' +
52 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info
Página 67
'</td>' +
'<td>' +
'<a href = "#" onclick = "cancelar suscripción (\' ' + símbolo +
'\'); "class =" btn btn-danger "> Eliminar </a> </td> ' ;
if ( ! $ ( '#norows' ). hasClass ( 'hidden' )) {
$ ( '#norows' ). addClass ( 'oculto' );
}
$ ( '# símbolo' ). val ( '' );
$ ( '# símbolo' ). foco ();
}
Lo primero que debe hacer cuando recibe una entrada del usuario es realizar la validación, que es
hecho para comprobar si ya tiene ese símbolo en su lista y devolver un error
si se encuentra. Si todo está bien, inicializa el símbolo en su lista de acciones y envía un nuevo
SUBSCRIBE marco al servidor. El resto del código es para la interfaz de usuario y agrega
una fila de la tabla con valores predeterminados mientras espera un valor legítimo del servidor.
Si un cliente puede suscribirse a una actualización de acciones, también debería poder cancelar la
suscripción.
Este siguiente fragmento hace exactamente eso, y se hace referencia en el código anterior para eliminarlo :
Objeto . tamaño = función ( obj ) {
var size = 0 , clave ;
para ( clave en obj ) {
if ( obj . hasOwnProperty ( clave )) tamaño ++ ;
}
tamaño de retorno ;
};
var unsubscribe = function ( símbolo ) {
Stomp . send_frame ( ws , {
"comando" : "CANCELAR SUSCRIPCIÓN" ,
"encabezados" : {
"destino" : "/ cola / existencias". + símbolo ,
},
contenido : ""
});
$ ( '#' + símbolo + '_row' ). eliminar ();
eliminar acciones [ símbolo ];
si ( Objeto . size ( stocks ) === 0 ) {
$ ( '#norows' ). removeClass ( 'oculto' );
}
}
Para darse de baja, realice las siguientes tareas:
1. Envíe el comando UNSUBSCRIBE en un marco STOMP con el símbolo como parte de
el destino.
Cliente | 53
www.it-ebooks.info

Página 68
2. Elimine la fila de la tabla en la interfaz de usuario.
3. Elimine la entrada en el objeto de existencias .
4. Compruebe si hay más símbolos en el objeto de existencias y, en caso contrario,
mostrar el bloque HTML #norows .
Las funciones de los dos fragmentos de código anteriores representan todas las acciones que puede
realizar un usuario
tome con su interfaz: suscríbase y cancele la suscripción. Ahora volvamos a la
función connect () , mostrada anteriormente, sin detalles sobre sus controladores. El primero es
la forma más elaborada usando la biblioteca stomp_helper.js para manejar eventos abiertos :
var onOpenHandler = function ( e ) {
Stomp . send_frame ( ws , {
"comando" : "CONECTAR" ,
"encabezados" : {
inicio de sesión : "websockets" ,
contraseña : "rabbitmq"
},
contenido : ""
});
}
En resumen, al obtener una conexión a su servidor WebSocket, envía su
Comando CONNECT con información de autenticación sobre el marco STOMP. En orden
para cerrar la conexión, sigue una ruta similar y proporciona una notificación para el
interfaz de usuario:
var online = false ;
var statusChange = function ( newStatus ) {
$ ( '# conexión' ). html (( newStatus ? 'Online' : 'Offline' ));
$ ( '# conexión' ). addClass (( newStatus ? 'btn-success' : 'btn-danger' ));
$ ( '# conexión' ). removeClass (( newStatus ? 'btn-danger' : 'btn-success' ));
online = newStatus ;
}
var switchOnlineStatus = function () {
if (en línea ) logoff (); más conectar ();
}
var logoff = function () {
statusChange ( falso );
Stomp . send_frame ( ws , {
"comando" : "DESCONECTAR"
}
);
devolver falso ;
}
54 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info

Página 69
El código HTML contiene un botón de estado que, al hacer clic en él, ejecutará
función switchOnlineStatus . Esto lo desconectará del servidor o
reconectarte como se vio anteriormente. La función de cierre de sesión envía su comando DESCONECTAR
utilizando un marco STOMP para decirle al servidor que realice sus propias rutinas de desconexión.
Todo el trabajo realizado en el servidor para recuperar existencias a través de RabbitMQ se coloca
en acción en el siguiente código. Como verá, su onMessageHandler toma datos
desde el servidor y actualiza la interfaz con los nuevos valores:
var updateStockPrice = function ( símbolo , originalValue , newValue ) {
var valElem = $ ( '#' + símbolo + 'intervalo' );
valElem . html ( newValue . toFixed ( 2 ));
var lostValue = ( newValue < originalValue );
valElem . addClass (( lostValue ? 'label-danger' : 'label-success' ))
valElem . removeClass (( lostValue ? 'label-success' : 'label-danger' ))
}
var onMessageHandler = function ( e ) {
frame = Stomp . process_frame ( p . ej . datos );
cambiar ( marco [ 'comando' ]) {
caso "CONECTADO" :
statusChange ( verdadero );
romper ;
caso "MENSAJE" :
var destino = marco [ 'encabezados' ] [ 'destino' ];
var contenido ;
prueba {
contenido = JSON . analizar ( marco [ 'contenido' ]);
} atrapar ( ex ) {
consola . log ( "excepción:" , ex );
}
var sub_stock = destino . subcadena (
destino . indexOf ( '.' ) + 1 , destino . longitud
);
updateStockPrice ( sub_stock , stocks [ sub_stock ], contenido . precio );
stocks [ sub_stock ] = contenido . precio ;
romper ;
}
}
Cuando se pasa un nuevo evento de mensaje , el código procesará esos datos como un STOMP
cuadro. El proceso consistirá en comprobar los comandos CONNECTED o MESSAGE
desde el marco. Los comandos que se procesarán incluyen los siguientes:
CONECTADO
Llame a statusChange (verdadero) para cambiar el estado del botón a "En línea"
MENSAJE
Recupere el encabezado de destino, analice el contenido y actualice el precio de las acciones en
La interfaz
Cliente | 55
www.it-ebooks.info
Página 70
El cliente tiene partes activas con la parte de suscripción / cancelación de suscripción / desconexión,
y las porciones pasivas que se encargan de recibir datos del servidor. El MENSAJE
Los eventos que se disparen estarán vinculados a un destino STOMP , y las existencias se actualizarán
ted en consecuencia en función de los datos recuperados.
Ha implementado con éxito las funciones más básicas disponibles en STOMP 1.0
protocolo. El mapeo entre STOMP y WebSocket puede ser simple, y hay
algunos comandos más que hemos dejado sin implementar en su proxy basado en nodos:
BEGIN , COMMIT , ACK , y en el servidor RECIBO .
El mapeo de STOMP sobre WebSocket logra dos cosas: le muestra cómo
protocolo diferente sobre WebSocket mediante el uso de la parte de subprotocolo de la especificación, y
permite hablar con un servidor AMQP sin necesidad específica de un componente de servidor
escrito. En la siguiente sección, aprenderá cómo conectarse a RabbitMQ con SockJS al
utilizando el complemento Web-Stomp con RabbitMQ. Aprenderá más sobre el uso de SockJS en
Capítulo 5, que cubre la compatibilidad con navegadores más antiguos. Hay varias opciones disponibles
capaz de enviar mensajes, incluidos estos populares:
• ActiveMQ
• ActiveMQ Apollo
• HornetQ

Usando RabbitMQ con Web-Stomp


A lo largo de este capítulo, ha estado escribiendo una implementación de servidor de STOMP para
de manera eficaz, envía comandos a RabbitMQ mediante AMQP. Esto con suerte ha demostrado
lo fácil que puede ser colocar otro protocolo encima de WebSocket. Ahora para redondear
al final del capítulo, aprenderá a configurar RabbitMQ con Web-Stomp, un complemento
que permite a RabbitMQ aceptar STOMP. El complemento expone un software compatible con SockJS.
puente sobre HTTP, que es una biblioteca de transporte alternativa (esto se analiza en más
detalle en el Capítulo 5 ). Mejora la compatibilidad para navegadores más antiguos que no tienen
soporte nativo para WebSocket.
Protocolo avanzado de Message Queue Server
El Protocolo de cola de mensajes avanzado (AMQP) es una aplicación estándar abierta
protocolo de capa para middleware orientado a mensajes. Las características definitorias de AMQP son
orientación de mensajes, colas, enrutamiento (incluidos punto a punto y publicación y
suscribirse), confiabilidad y seguridad.
56 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info

Página 71
Cliente STOMP para Web y Node.js
Para una implementación más completa de su trabajo en este capítulo, descargue el
STOMP a través de la biblioteca WebSocket . Proporciona una biblioteca cliente de JavaScript para
acceder
servidores que utilizan STOMP 1.0 y 1.1 a través de WebSocket, y una biblioteca Node.js para realizar
lo mismo en WebSocket junto con una opción para sockets TCP a través de STOMP.
Instalación del complemento Web-Stomp
Editemos ese script de shell de aprovisionamiento que se usó anteriormente en el capítulo para configurar
RabbitMQ.
En el script, después de detener el servidor RabbitMQ durante la instalación, agregará el
siguiente línea:
rabbitmq-plugins habilitan rabbitmq_web_stomp
Además, su máquina virtual necesita edición, así que reenvíe el puerto 15674, que es
abierto por el complemento previamente instalado para escuchar las solicitudes de SockJS. Modificarás
el Vagrantfile existente y agregue la siguiente línea con todas las demás configuraciones de red
opciones:
config.vm.network: puerto_enviado, invitado: 15674, host: 15674
Después de hacerlo, si la instancia de VirtualBox original aún se está ejecutando, puede ejecutar
vagabundo detener o vagabundo destruir , y luego volver a correr vagabundo para recrear el
ejemplo. Si ha destruido, entonces ha terminado, y abrirá el nuevo puerto y se convertirá
en el nuevo complemento. Si se ha detenido, puede realizar las siguientes tareas:
vagabundo ssh
sudo su -
rabbitmq-plugins habilitan rabbitmq_web_stomp
Esto habilita un nuevo complemento llamado Web-Stomp y expone el puerto 15674. Rabbit tiene
estandarizado en el uso de SockJS para todas las comunicaciones de WebSocket, y discutiremos
esa biblioteca más adelante en el Capítulo 5. Para continuar, querrá descargar JavaScript
Biblioteca STOMP disponible en stomp.js . Entonces puedes seguir cambiando de cliente
código para utilizar el punto final Web-Stomp.
Cliente Echo para Web-Stomp
Construyamos un cliente de eco simple que se suscribe a una cola llamada / tema / eco y
luego envía y recibe mensajes. En la parte superior de su archivo HTML, incluya lo siguiente
ing declaraciones de JavaScript:
< script src = "http://cdn.sockjs.org/sockjs-0.3.min.js" > < / script>
< script src = "stomp.min.js" > < / script>
Puede elegir descargar la versión minimizada como se menciona en este código, o la
versión no minimizada si lo prefiere. En cualquier caso, puede descargar el stomp-
biblioteca websocket en GitHub.
Uso de RabbitMQ con Web-Stomp | 57
www.it-ebooks.info

Página 72
Su HTML será casi idéntico al ejemplo anterior de echo , y modificará
el JavaScript para satisfacer sus necesidades mediante el complemento RabbitMQ Web-Stomp y el
Biblioteca Stomp.js:
<! DOCTYPE html>
<html> <cabeza>
<title> Echo Server </title>
</head>
<body lang = "en" >
<h1> Servidor Web Stomp Echo </h1>
<ul id = "mensajes" >
</ul>
<formulario onsubmit = "send_message (); return false;" >
<input type = "text" name = "message" style = "width: 200px;"
id = "message" placeholder = "Escriba el texto para hacer eco aquí"
valor = "" enfoque automático />
<input type = "button" value = "¡Enviar!" onclick = "send_message ();" />
</form>
</body>
</html>
Su primera tarea es inicializar el punto final RabbitMQ SockJS y luego pasarlo al
Biblioteca de JavaScript STOMP. La biblioteca Stomp.js le permite utilizar WebSocket nativo,
o cualquier cosa que ofrezca la misma API como SockJS. Porque SockJS no ofrece
soporte de latido del corazón, lo mantendrá apagado. La biblioteca Stomp.js ofrece varias oportunidades
tunities para la devolución de llamada y para realizar cualquier tarea que desee con los datos que
Vuelve. Aquí, solo está enviando los datos a la consola:
var ws = new SockJS ( 'http: // localhost: 15674 / stomp' );
var cliente = Stomp . sobre ( ws );
cliente . latido del corazón . saliente = 0 ;
cliente . latido del corazón . entrante = 0 ;
cliente . debug = function ( str ) {
consola . log ( str );
}
Cuando se conecte a una cola de RabbitMQ, simplemente ofrecerá detalles de inicio de sesión y algunos
devoluciones de llamada junto con el host (o virtualhost en términos de RabbitMQ). El append_log
La función será idéntica a la mostrada anteriormente, pero implementando las devoluciones de llamada
requerido para conexión , error y una nueva función send_message se muestra aquí:
cliente . connect ( 'websockets' , 'rabbitmq' , connect_callback , error_callback . '/' );
var connect_callback = function ( x ) {
id = cliente . subscribe ( "/ topic / echo" , function ( mensaje ) {
58 | Capítulo 4: STOMP sobre WebSocket
www.it-ebooks.info

Página 73
append_log ( mensaje . cuerpo );
consola . log ( JSON . stringify ( mensaje . cuerpo ));
});
};
var error_callback = function ( error ) {
consola . log ( error . cabeceras . de mensaje );
};
En connect_callback , emite un comando de suscripción para la cola / tema / eco, por lo que
cualquier mensaje que aparezca en ese contenedor se agregará al área de texto de la interfaz de
usuario. los
la implementación de error_callback simplemente envía cualquier error recibido a la consola
para depurar según sea necesario.
Ahora tiene un cliente que hará eco de los mensajes vertidos en la cola de un área de texto.
A continuación, conectará el proceso de envío a una nueva función send_message que
se parece mucho a la versión de WebSocket:
var send_message = function ( data ) {
cliente . enviar ( "/ tema / eco" , {}, documento . getElementById ( 'mensaje' ). valor );
};
La principal diferencia aquí es que en lugar de simplemente enviar a través de WebSocket,
proporcione la cola (destino) y encabezados adicionales, de los cuales no pasa ninguno en este
ejemplo.

Resumen
En este capítulo, creó un subprotocolo sobre WebSocket para STOMP 1.0. Como el
servidor se construyó, el cliente evolucionó para admitir los comandos necesarios a lo largo del cable para
Apoyar el protocolo. Al final, aunque el cliente que creó no es totalmente compatible con todos los
STOMP 1.0, le permitió ser testigo de lo fácil que es colocar otro protocolo encima
de WebSocket y conéctelo a un agente de mensajes como RabbitMQ.
Como vio en el Capítulo 2 , implementar STOMP sobre WebSocket es uno de los “Regis‐
Protocolos Tered ”(y también cae bajo un“ Protocolo Abierto ”). Nada te detiene
de utilizar la información de este capítulo para crear su propio protocolo de comunicación
catión, porque la especificación WebSocket es totalmente compatible con esto.
El siguiente capítulo explora los problemas de compatibilidad que enfrenta al elegir
implementar WebSocket, y cómo asegurarse de que puede comenzar a usar el poder de Web‐
Socket hoy.
Resumen | 59
www.it-ebooks.info

Página 74
www.it-ebooks.info

Página 75
CAPÍTULO 5
Compatibilidad con WebSocket
La tecnología detrás de WebSocket es permitir la comunicación bidireccional entre
cliente y servidor. Una implementación nativa de WebSocket minimiza los recursos del servidor
uso y proporciona un método coherente de comunicación entre el cliente y el servidor.
Al igual que con la adopción de HTML5 en los navegadores de los clientes, el panorama del soporte es
relevante
cerrado a los navegadores modernos. Eso significa que no hay soporte para ningún usuario con Internet.
Explorer menos de 10 y el navegador móvil admite menos que iOS Safari 6 y Chrome
para Android.
Estas son solo algunas de las versiones compatibles con RFC 6455 WebSocket:
• Internet Explorer 10
• Firefox 6
• Cromo 14
• Safari 6.0
• Opera 12.1
• iOS Safari 6.0
• Chrome para Android 27.0
Este capítulo describe las opciones para admitir navegadores más antiguos que son anteriores a la Web.
Socket RFC 6455 spec cuando desee aprovechar la comunicación bidireccional
ción en su aplicación. Las plataformas que verá resuelven problemas de compatibilidad con
navegadores de clientes más antiguos y agregue una capa de organización para sus mensajes.
61
www.it-ebooks.info

Página 76
SockJS
SockJS es una biblioteca de JavaScript que proporciona un objeto similar a WebSocket en el navegador.
La biblioteca es compatible con muchos más navegadores debido a su uso condicional de múltiples
múltiples transportes de navegador. Utilizará WebSocket si la opción está disponible como primera
elección. Si una conexión nativa no está disponible, puede recurrir a la transmisión y, finalmente,
sondeo si eso tampoco está disponible. Esto proporciona un navegador casi completo y restrictivo.
soporte de proxy, como se muestra en la Tabla 5-1.
Tabla 5-1. Transportes compatibles
Navegador
WebSockets
Transmisión
Votación
IE 6, 7
No
No
jsonp-polling
IE 8, 9 (cookies = no) No
xdr-streaming
xdr-polling
IE 8, 9 (cookies = sí) No
iframe-htmlfile
iframe-xhr-polling
IE 10
rfc6455
xhr-streaming
xhr-polling
Cromo 6-13
hixie-76
xhr-streaming
xhr-polling
Chrome 14+
hybi-10 / rfc6455 xhr-streaming
xhr-polling
Firefox <10
No
xhr-streaming
xhr-polling
Firefox 10+
hybi-10 / rfc6455 xhr-streaming
xhr-polling
Safari 5
hixie-76
xhr-streaming
xhr-polling
Opera 10.70+
No
iframe-eventsource iframe-xhr-polling
Konqueror
No
No
jsonp-polling
Para utilizar completamente la biblioteca SockJS, necesita una contraparte de servidor. La biblioteca tiene
varios
opciones para la contraparte del servidor, y se escriben más todo el tiempo. Lo que sigue es
una muestra de algunas de las bibliotecas de servidor disponibles:
• Nodo SockJS
• SockJS-erlang
• SockJS-tornado
• SockJS-twisted
• SockJS-ruby
62 | Capítulo 5: Compatibilidad con WebSocket
www.it-ebooks.info

Página 77
• SockJS-netty
• SockJS-gevent (horquilla SockJS-gevent)
• SockJS-go
Para nuestras necesidades, nos quedaremos con una solución totalmente JavaScript.
Servidor de chat SockJS
Vas a volver a visitar tu aplicación de chat y realizar cambios para usar SockJS
bibliotecas para servidor y cliente.
Como se mencionó, para poder utilizar completamente la biblioteca cliente SockJS en el navegador, debe
requieren un componente de servidor válido:
var express = require ( 'express' );
var http = require ( 'http' );
var sockjs = require ( 'sockjs' );
var uuid = require ( 'uuid' );
Su lista de nuevas bibliotecas ahora incluye SockJS , http del estándar Node.js
biblioteca y Express.
Node.js tiene un administrador de paquetes completamente desarrollado con npm. Son
normalmente se instalan juntos, y una simple llamada a npm install [pack
age] desplegará la última revisión. La instalación creará un
directorio node_modules si no existe, y coloque los módulos
dentro. Si desea instalar el módulo globalmente, puede usar el -
g bandera. Para obtener más información, consulte los documentos .
Estas dependencias no estarán disponibles en Node.js de forma predeterminada, así que ejecute lo
siguiente
comandos para instalarlos:
npm instalar sockjs
npm install express
A continuación, creará un objeto SockJS y escuchará el evento de conexión . Los eventos
utilizados con SockJS-node son ligeramente diferentes a los similares de WebSocket
clientela:
• conexión
• datos (equivalente a mensaje con WebSocket)
• cerrar
• error
SockJS | 63
www.it-ebooks.info

Página 78
Express hace algo interesante con su biblioteca exportando una función como
interfaz a su módulo. Se utiliza para crear una nueva aplicación Express y se puede
escrito de dos maneras:
var app = express ();
O mucho más escueto:
var express = require ( 'express' ) ();
Esto crea una aplicación Express y le permite asignarla a la variable derecha
lejos. Detrás de escena, hay algo de magia de JavaScript al asignar el
función a module.exports :
exportaciones = módulo . exportaciones = createApplication ;
...
function createApplication () {
...
}
Ahora puede crear su nuevo servidor SockJS inicializando express , creando un
httpServer con la aplicación express , y finalmente, creando un servidor SockJS que
escucha el evento de conexión :
var app = express ();
var httpServer = http . createServer ( aplicación );
var sockServer = sockjs . createServer ();
sockServer . on ( 'conexión' , función ( conn ) {
...
conn . on ( 'mensaje' , función ( mensaje ) {
if ( mensaje . indexOf ( '/ nick' ) === 0 ) {
var nickname_array = mensaje . dividir ( '' );
if ( nickname_array . length > = 2 ) {
var old_nickname = apodo ;
nickname = nickname_array [ 1 ];
var nickname_message = "Client" + old_nickname + "cambiado a"
+ apodo ;
wsSend ( "nick_update" , client_uuid , nickname , nickname_message );
}
} más {
wsSend ( "mensaje" , client_uuid , apodo , mensaje );
}
});
...
}
El único cambio en el manejo de eventos de su código anterior es escuchar un
evento llamado datos en lugar de mensaje . Además, realiza un ligero ajuste a
su método wsSend para tener en cuenta las diferencias con la API de SockJS:
64 | Capítulo 5: Compatibilidad con WebSocket
www.it-ebooks.info

Página 79
var CONECTANDO = 0 ;
var ABIERTO = 1 ;
var CIERRE = 2 ;
var CERRADO = 3 ;
function wsSend ( tipo , client_uuid , nickname , message ) {
para ( var i = 0 ; i < clientes . longitud ; i ++ ) {
var clientSocket = clientes [ i ]. la conexión ;
if ( clientSocket . readyState === OPEN ) {
clientSocket . escribir ( JSON . stringify ({
"tipo" : tipo ,
"id" : client_uuid ,
"apodo" : apodo ,
"mensaje" : mensaje
}));
}
}
}
El objeto WebSocket que usó anteriormente tenía constantes para la propiedad readyState
erty, pero aquí los definirás en tu código de cliente (para evitar ensuciar el código con
enteros). El objeto de conexión SockJS tiene la misma propiedad readyState , y usted
lo comparará con la constante OPEN , que tiene un valor de 1 . El otro gran cambio es
el método para enviar datos de vuelta al cliente, que es .write (mensaje) en su lugar
de .send (mensaje) .
Ahora que ha convertido todo desde la versión de WebSocket para usar SockJS-
código específico, inicializará una nueva aplicación con Express y vinculará el prefijo / chat a
su instancia http.Server :
var app = express ();
var httpServer = http . createServer ( aplicación );
sockServer . installHandlers ( httpServer , { prefijo : '/ chat' });
httpServer . escuchar ( 8181 , '0.0.0.0' );
El servidor HTTP escuchará en el puerto 8181 y responderá a las solicitudes que escuchen en cualquier IP
de la máquina, como denota 0.0.0.0 .
En el ejemplo del Capítulo 3 , abrió su archivo HTML sin un servidor HTTP
presente. Con SockJS y las otras alternativas de este capítulo, optará por servir
el cliente y el servidor desde el mismo servidor HTTP. Aquí configuras tu client.html
y style.css para ser enviado de vuelta a solicitud a http: // localhost: 8181 / client.html :
expreso . get ( '/client.html' , function ( req , res ) {
res . sendfile ( __dirname + '/client.html' );
});
expreso . get ( '/style.css' , function ( req , res ) {
res . sendfile ( __dirname + '/style.css' );
});
SockJS | sesenta y cinco
www.it-ebooks.info
Página 80
Ahora ha convertido con éxito el servidor WebSocket simple en uno que usa el
Biblioteca SockJS.
Cliente de chat SockJS
Veamos cómo convertir el cliente para usar la biblioteca SockJS. La primera cosa
que necesitará al comienzo de cualquier otro JavaScript será incluir el SockJS
biblioteca:
<script src = "http://cdn.sockjs.org/sockjs-0.3.min.js" > </script>
Esta biblioteca proporciona el objeto SockJS, que imita la biblioteca WebSocket
incluido en la mayoría de los navegadores modernos. La inicialización también cambia porque está
no usando el protocolo ws o wss , sino usando http como transporte inicial:
var sockjs = new SockJS ( "http://127.0.0.1:8181/chat" );
Para su código de cliente de WebSocket, usó el nombre de variable ws . Aqui parece mas
apropiado para cambiarle el nombre a sockjs . Encuentre todas las instancias de uso de ws en el código de
Capítulo 3 y reemplácelos con sockjs . Ese es el alcance de los cambios necesarios
para el cliente. SockJS ofrece una buena migración desde WebSocket nativo a
la biblioteca SockJS.
SockJS ofrece soporte para uno o más protocolos de transmisión para todos los navegadores principales,
que funcionan entre dominios y admiten cookies. Los transportes de votación se utilizarán en
el caso de navegadores y hosts más antiguos con proxies restrictivos como alternativa viable.
A continuación, cambiará su aplicación de chat para usar la plataforma Socket.IO
en lugar.

Socket.IO
Usar WebSocket directamente es una decisión fácil cuando puede controlar los clientes que están
usando su sistema. Dado que la mayoría de las organizaciones tienen que atender a un cliente heterogéneo
entorno, otra alternativa es Socket.IO . El desarrollo detrás de Socket.IO
busca hacer posibles las aplicaciones en tiempo real independientemente del navegador.
La biblioteca es capaz de realizar esta hazaña recurriendo con gracia a diferentes tecnologías
gies que realizan cosas similares. Los transportes utilizados en caso de que WebSocket sea
no disponibles en el cliente incluyen lo siguiente:
• Adobe Flash Socket
• Sondeo largo Ajax
• Transmisión multiparte Ajax
• Iframe permanente
66 | Capítulo 5: Compatibilidad con WebSocket
www.it-ebooks.info

Página 81
• Sondeo JSONP
Usar la implementación nativa de WebSocket sería similar a usar TCP directamente para
comunicar. Ciertamente es posible hacerlo, y quizás en la mayoría de los casos el
elección, pero no hay vergüenza en usar un marco para hacer algo del trabajo pesado para
tú. De forma predeterminada, Socket.IO utilizará una conexión WebSocket nativa si el navegador
La rogación lo considera posible.
Adobe Flash Socket
Uno de los transportes alternativos proporcionados por Socket.IO es Adobe Flash Socket. Esta
permite utilizar una conexión similar a WebSocket sobre Adobe Flash en lugar de nativa
apoyo. Esto tiene la ventaja de una conexión de enchufe, con muy pocos inconvenientes. Cómo-
Sin embargo, uno de los inconvenientes es que es necesario abrir otro puerto para el servidor de políticas.
De forma predeterminada, Socket.IO verificará el puerto 10843 e intentará usarlo si está disponible.
Conectando
La conexión a un servidor Socket.IO se logra primero tomando las bibliotecas cliente. Si
el cliente que estás usando es JavaScript, el método más simple para hacer esto es simplemente
haciendo referencia al servidor Socket.IO e incluyendo el archivo socket.io.js :
<script src = "http: // localhost: 8181 / socket.io / socket.io.js" > </script>
La ruta más sencilla para servir la biblioteca cliente es desde el propio servidor Socket.IO. Si tu
servidor web y Socket.IO están siendo atendidos por el mismo host y puerto, puede
omitir el host y el puerto de la llamada y hacer referencia a él como cualquier otro archivo servido desde
el servidor web. Para servir la biblioteca de cliente Socket.IO en el mismo host y puerto, deberá
tiene que configurar su servidor web para reenviar solicitudes al servidor Socket.IO,
o clonar el repositorio socket.io-client y coloque los archivos donde desee.
Si desea almacenar en caché agresivamente la biblioteca cliente Socket.IO, una configuración adicional
lo que puede hacer es incluir el número de versión en la solicitud así:
<script src = "/socket.io/socket.io.v1.0.js" > </script>
Como comentamos en el Capítulo 2, WebSocket utiliza cuatro eventos o "marcos de control". Con
Socket.IO, todo es mucho más abierto en el departamento de eventos. El seguimiento-
Los eventos de ing se disparan desde el propio marco:
conexión
La conexión inicial de un cliente que proporciona un argumento de socket , que puede
ser utilizado para futuras comunicaciones con el cliente.
mensaje
El evento que emite cuando el cliente invoca socket.send .
Socket.IO | 67
www.it-ebooks.info

Página 82
desconectar
El evento que se dispara cada vez que se cierra la conexión cliente-servidor.
cualquier cosa
Cualquier evento excepto los reservados enumerados. El argumento de datos son los datos enviados,
y la devolución de llamada se utiliza para enviar una respuesta.
Lo primero es lo primero. Después de incluir la biblioteca cliente de JavaScript, debe abrir una
conexión al servidor:
var socket = io . conectar ( 'http: // localhost: 8181' );
Ahora que tiene una conexión Socket.IO, puede comenzar a escuchar eventos específicos
que se emitirá desde el servidor. Su aplicación cliente puede escuchar cualquier nombre
evento proveniente del punto final, y también puede emitir sus propios eventos para ser escuchados
y reaccionó desde el lado del servidor.
Servidor de chat Socket.IO
Revisemos nuevamente el ejemplo del chat. Copie su código de SockJS en su mayoría literalmente,
y realizar una inicialización similar a la biblioteca anterior:
var socketio = require ( 'socket.io' );
...
var app = express ();
var httpServer = http . createServer ( aplicación );
var io = socketio . escuchar ( servidor );
Debido a que Socket.IO usa nombres abiertos para eventos, no hay necesidad de calzar
diferentes eventos entrantes dentro de la misma construcción de mensaje . Por tanto, con tu
El código Socket.IO divide los mensajes y las solicitudes de apodo en eventos separados:
conn . on ( 'mensaje' , función ( datos ) {
wsSend ( "mensaje" , client_uuid , apodo , mensaje );
});
...
conn . on ( 'apodo' , función ( apodo ) {
var old_nickname = apodo ;
apodo = apodo . apodo ;
var nickname_message = "Client" + old_nickname + "cambiado a" + nickname ;
wsSend ( 'nickname' , client_uuid , nickname , nickname_message );
})
68 | Capítulo 5: Compatibilidad con WebSocket
www.it-ebooks.info

Página 83
Ha enviado el código para analizar una solicitud de apodo al cliente, y también puede escuchar
diez para un evento separado enviado desde el servidor para mensajes específicos de apodos y logi‐
trátelos de manera diferente si así lo desea.
Cliente de chat Socket.IO
Cuando el cliente quiere comunicarse con el servidor, realiza la misma API
y emite un evento con nombre que el servidor puede escuchar. Debido a la naturaleza de
sirviendo el HTML Socket.IO en el mismo servidor HTTP, puede hacer referencia
Socket.IO del mismo dominio sin especificar:
<script src = "/socket.io/socket.io.js" > </script>
Con SockJS, mapea de cerca la especificación nativa de WebSocket. Con Socket.IO, el único
la similitud es escuchar eventos y enviar eventos de vuelta al servidor. Un numero de
Los eventos se disparan desde el marco Socket.IO, lo que debería ayudarlo a mantenerse conectado
ted y conocedor de la conexión y el estado:
conectar
Emitido cuando la conexión con el servidor es exitosa
conectando
Emitido cuando se intenta la conexión con el servidor
desconectar
Emitido cuando se ha desconectado la conexión con el servidor
Conexión fallida
Emitido cuando Socket.IO no ha podido establecer una conexión con todos y cada uno
mecanismos de transporte para retroceder
error
Emitido cuando ocurre un error que no es manejado por otros tipos de eventos
mensaje
Emitido cuando se recibe un mensaje a través de un socket.send y callback es un
función de reconocimiento opcional
reconnect_failed
Emitido cuando Socket.IO no puede restablecer una conexión funcional después de lacon‐
gotas de neccion
reconectarse
Emitido cuando Socket.IO se vuelve a conectar con éxito al servidor.
reconectando
Emitido cuando Socket.IO intenta volver a conectarse con el servidor
Socket.IO | 69
www.it-ebooks.info

Página 84
cualquier cosa
Cualquier evento excepto los reservados enumerados. El argumento de datos son los datos enviados y
la devolución de llamada se utiliza para enviar una respuesta
Además, como comentamos anteriormente, socket.io-client está disponible si desea servir
la biblioteca sin utilizar el mecanismo habitual.
En su nueva base de código de cliente, el tamaño crece un poco para manejar la inserción del apodo
comando analizando a la interfaz y emitiendo su nuevo apodo de evento :
function sendMessage () {
var messageField = documento . getElementById ( 'mensaje' );
var message = messageField . valor ;
if ( mensaje . indexOf ( '/ nick' ) === 0 ) {
var nickname_array = mensaje . dividir ( '' );
if ( nickname_array . length > = 2 ) {
zócalo . emit ( 'apodo' , {
apodo : nickname_array [ 1 ]
});
}
} más {
zócalo . enviar ( valor de messageField . );
}
messageField . valor = '' ;
messageField . foco ();
}
Como puede ver, ha movido el código originalmente en el servidor al extremo del cliente,
y están utilizando la llamada socket.emit (canal, datos) de Socket.IO para enviar su
cambio de apodo en el servidor.
Todo lo demás en el cliente es prácticamente igual. Usas el método de Socket.IO
en (canal, datos) para escuchar eventos específicos (reservados o no) y procesar
ellos como de costumbre.
Ahora que ha escrito su primer proyecto Socket.IO, puede consultar la documentación
mentación y revisar las características adicionales que proporciona además de WebSocket y qué
hemos discutido.
Pasemos a un proyecto más, que es de carácter comercial y en el mismo
acampa como Socket.IO en términos de las características agregadas y el valor que proporciona además
de
implementación nativa de WebSocket.

Pusher.com
La última opción que verá es una capa que se coloca encima de WebSocket y ofrece la
alternativas que ha visto en otras soluciones. El equipo detrás de Pusher ha construido un
impresionante lista de funciones para usar con su aplicación si decide usar sus
Servicio. Al igual que las otras dos soluciones, Pusher ha implementado una capa
70 | Capítulo 5: Compatibilidad con WebSocket
www.it-ebooks.info

Página 85
sobre WebSocket a través de su API junto con un método de prueba para un respaldo aceptable
métodos si los demás fallan.
La API puede realizar los fallbacks de manera muy similar a Socket.IO probando
Compatibilidad con WebSocket y, en caso de que falle, utilizar el popular cliente web-socket.js,
que sustituye un objeto Flash por el soporte de WebSocket en el navegador. Si Flash no es
instalado, o los firewalls o proxies impiden una conexión exitosa, el respaldo final
utiliza transportes basados en HTTP.
Similar a los eventos que están en la parte superior del transporte de Socket.IO, la API Pusher tiene
algunas
más trucos bajo la manga. Cuenta con canales de tipo público y privado, que
le permite filtrar y controlar la comunicación con el servidor. Un tipo especial de cambio
nel también está disponible para presencia , donde el cliente puede registrar datos de miembros para
mostrar
Estado en línea.
La principal diferencia aquí es que está incluyendo a un tercero en su comunicación.
ción entre el servidor y el cliente. Su servidor recibirá comunicación del cliente
código probablemente usando una simple llamada Ajax desde HTML, y en base a eso, a su vez
utilice la API REST de Pusher.com para activar eventos. El cliente se conectará a
Pusher.com con suerte a través de WebSocket si está disponible dentro del navegador, o uno de
los métodos de reserva y recibir eventos activados contra la aplicación. Solo con varios
Las restricciones cumplidas ¿Puede un cliente desencadenar eventos y pasarlos a través de la red sin
pasando primero por su propia API de servidor.
Repasemos algunos de los aspectos particulares de la API de Pusher.com, porque son
bastante extenso.
Canales
El uso de WebSocket nativo es una forma perfecta de lograr una comunicación bidireccional con
el entendimiento de que los clientes deben admitir el protocolo WebSocket, que puede
superar cualquier problema de proxy, y que creará cualquier código de infraestructura necesario
sario para hacer la vida más fácil en el backend. Obtendrá un flujo de datos del texto o
marco de mensaje binario, y depende de usted analizarlo, darle sentido y pasarlo a
cualquier controlador que haya configurado en su código.
La API Pusher le proporciona una buena parte de esto. Los canales son un programa común‐
ming y se utiliza con esta API para filtrar datos y controlar el acceso. UNA
El canal surge simplemente con un cliente que se suscribe y vincula
eventos a ella.
Pusher tiene bibliotecas para muchos de los principales marcos y lenguajes que son populares
hoy. Nos centramos, como siempre, en JavaScript. Aquí verá cómo suscribirse a un canal
nel llamado channelName . Una vez que tenga su variable de canal , puede usarla para
enviar y recibir eventos.
Pusher.com | 71
www.it-ebooks.info

Página 86
Con la mayoría de las operaciones del canal, puede vincularse a un evento que le notificará
el éxito o el fracaso de la suscripción : empujador: subscription_succeeded :
var canal = empujador . suscribirse ( channelName );
De esta manera, ha creado un canal con nombre público que cualquier cliente que se conecte al
el servidor puede suscribirse o cancelar la suscripción. Y cancelar la suscripción también es tan simple
como
podrían hacerlo. Solo proporcione el nombre del canal y la API cancelará su suscripción
de escuchar en ese canal:
empujador . cancelar suscripción ( channelName );
La API también proporciona suscripción a canales privados. El permiso debe ser de autor
izado a través de una URL de autenticación solicitada por HTTP. Todos los canales privados tienen
prefijo
con privado como convención de nomenclatura, como se muestra en el siguiente ejemplo de código. los
la autenticación puede ocurrir a través de Ajax o JSONP:
var privateChannelName = "private-mySensitiveChannelName" ;
var privateChannel = empujador . subscribe ( privateChannelName );
Una de las funciones más necesarias cuando se utiliza la comunicación bidireccional es el estado
gestión de la presencia de miembros . Pusher proporciona esto con llamadas especializadas para
presencia del usuario junto con eventos para escuchar para garantizar la integridad.
Los siguientes eventos son los que puede escuchar para asegurarse de que se cumplieron las expectativas:
empujador: subscription_succeeded
Común en todas las llamadas de canal. La vinculación a este evento le permite asegurarse de que un sub‐
la inscripción ha tenido éxito.
empujador: subscription_error
Enlace a este evento para recibir una notificación cuando falle una suscripción.
empujador: member_added
Este evento se activa cuando un usuario se une a un canal. Este evento se dispara solo una vez
por usuario único, incluso si un usuario se ha unido a varios canales de presencia.
empujador: member_removed
Este evento se activa cuando un usuario abandona un canal. Porque un usuario puede unirse
varios canales, este evento se activa solo cuando se cierra el último canal.
Eventos
Los eventos en Pusher son la forma en que los mensajes se transmiten desde el servidor
y el cliente. Un canal, ya sea público o privado, puede realizar eventos que pasan este
datos hasta el cliente. Si busca filtrar mensajes en diferentes depósitos, los eventos
no son el camino, pero los canales sí. Los eventos en Pusher se nombran adecuadamente en tiempo
pasado.
porque son notificaciones de cosas que sucedieron en el sistema.
72 | Capítulo 5: Compatibilidad con WebSocket
www.it-ebooks.info

Página 87
Si tiene un canal llamado chat , querrá estar al tanto de los mensajes nuevos
estaban ocurriendo para que pudiera pintar eso en la GUI:
var pusher = new Pusher ( 'APP_KEY' );
var canal = empujador . subscribe ( 'chat-websocket' );
canal . bind ( 'mensaje nuevo' , función ( datos ) {
// agrega cualquier mensaje nuevo a nuestra colección
}
);
No se requiere la vinculación a través de un canal. Con la misma facilidad con que se enlazó a un canal de
disparo
eventos, puede hacerlo usando la variable de empujador raíz :
var pusher = new Pusher ( 'APP_KEY' );
empujador . bind ( eventName , function ( data ) {
// procesar los datos de eventName
});
Obviamente, la API para Pusher y los patrones de uso que puede tener son bastante amplios. los
Pusher API está bien diseñada y es capaz de procesar una increíble cantidad de mensajes por
día y número de conexiones simultáneas. En la siguiente sección, realizará la
mismo ejercicio que hizo anteriormente con Socket.IO y cree una pequeña aplicación de chat
mediante la API Pusher.
Servidor de chat Pusher
Ha escrito una aplicación de chat simple usando Socket.IO y SocksJS, y ahora está
tiempo para tomar el conocimiento que ha adquirido de la API de Pusher.com y la forma de hacer
cosas y reescribir el chat. La principal diferencia es que enviar sus mensajes de chat
desde el cliente se realizará a través de una API que ha preparado en su servidor. Todo activado
los eventos en Pusher.com ocurren a través de su servidor, y los eventos vinculados en los canales son
pasado de Pusher.com al cliente mediante WebSocket o el respaldo.
Primero describamos su servidor, incluido un shell de las llamadas a la API y las dependencias
Necesitarás. Lo primero es lo primero, debe instalar las dependencias de su nodo usando npm:
$ npm instalar node-uuid
$ npm instalar empujador
$ npm install express
$ npm instalar body-parser
Ha instalado y utilizado node-uuid en varios otros ejemplos de servidor. Esta sección es
obviamente sobre la API de Pusher.com, por lo que instalará su biblioteca Node.js. En
Para escuchar y analizar el cuerpo de los mensajes como JSON, está utilizando express y
analizador corporal .
Aquí hay un shell de cómo se ve su servidor:
var express = require ( 'express' );
var http = require ( 'http' );
Pusher.com | 73
www.it-ebooks.info

Página 88
var Pusher = require ( 'empujador' );
var uuid = require ( 'node-uuid' );
var bodyParser = require ( 'body-parser' );
var app = express ();
aplicación . use ( bodyParser . json ());
var httpServer = http . createServer ( aplicación );
var pusher = new Pusher ({
appId : 'YOUR-APP-ID' ,
clave : 'YOUR-APP-KEY' ,
secreto : 'YOUR-APP-SECRET'
});
var clientes = {};
var clientIndex = 1 ;
función sendMessage ( tipo , client_uuid , apodo , mensaje ) {
}
aplicación . publicación ( "/ apodo" , función ( req , res ) {
});
aplicación . post ( "/ login" , function ( req , res ) {
});
aplicación . publicar ( "/ chat" , función ( req , res ) {
});
aplicación . escuchar ( 8181 );
aplicación . get ( '/client.html' , function ( req , res ) {
res . sendfile ( __dirname + '/client.html' );
});
Como puede ver, ha requerido e incluido sus dependencias, ha creado express
con el analizador corporal, y consiguió que escuchara en el puerto 8181 y sirviera a su cliente
plato. Su API consta de las llamadas enumeradas en la Tabla 5-2.
Tabla 5-2. Llamadas a la API
Punto final del método HTTP
Descripción
ENVIAR
/ nickname Actualiza el apodo del cliente y notifica a todos los clientes conectados
ENVIAR
/iniciar sesión
Conexión inicial que asigna un apodo anónimo y una identificación de cliente única
ENVIAR
/charla
Los mensajes del chat se transmiten junto con el apodo y la identificación del cliente.
74 | Capítulo 5: Compatibilidad con WebSocket
www.it-ebooks.info

Página 89
La llamada sendMessage no forma parte de la API, sino una función de conveniencia utilizada por varios
de los ejemplos. Activa un evento de tipo en el chat del canal , que ha vinculado
al iniciar el servidor. El JSON que está pasando para todos los mensajes incluye el
ID de cliente , apodo si corresponde y mensaje :
función sendMessage ( tipo , client_uuid , apodo , mensaje ) {
empujador . trigger ( 'chat' , type , {
"id" : client_uuid ,
"apodo" : apodo ,
"mensaje" : mensaje
});
}
La primera llamada a la API que se espera que realice un cliente es iniciar sesión . El cliente recibirá un
identificador único en forma de uuid y un apodo indexado único:
aplicación . post ( "/ login" , function ( req , res ) {
var client_uuid = UUID . v4 ();
var nickname = "AnonymousUser" + clientIndex ;
clientIndex + = 1 ;
clientes [ client_uuid ] = {
'id' : client_uuid ,
'nickname' : apodo
};
res . estado ( 200 ). enviar (
JSON . stringify ( clientes [ client_uuid ])
);
});
Es probable que sus clientes quieran que sus propios apodos estén representados en la aplicación de chat.
ción. Una llamada a / nickname hará el cambio solicitado y activará un nick de evento
name para permitir que los clientes muestren el cambio en la interfaz:
aplicación . publicación ( "/ apodo" , función ( req , res ) {
var old_nick = clientes [ req . cuerpo . id ]. apodo ;
var nickname = req . cuerpo . apodo ;
clientes [ req . cuerpo . id ]. apodo = apodo ;
sendMessage ( 'apodo' ,
req . cuerpo . id ,
apodo ,
old_nick + "cambió el apodo a" + apodo );
res . estado ( 200 ). enviar ( '' );
});
Pusher.com | 75
www.it-ebooks.info

Página 90
El más simple de todos es el mensaje de chat. Aceptas la identificación del cliente , agarras el nick
nombre de su matriz existente, y use el mensaje pasado en el JSON y active un
mensaje de evento hasta el canal de chat Pusher.com:
aplicación . publicar ( "/ chat" , función ( req , res ) {
sendMessage ( 'mensaje' ,
req . cuerpo . id ,
clientes [ req . cuerpo . id ]. apodo ,
req . cuerpo . mensaje );
res . estado ( 200 ). enviar ( '' );
});

Cliente de chat Pusher


Su servidor ahora está esperando que un cliente se conecte. Utilizará el mismo tema HTML
plate como en capítulos anteriores, y usando el HTML de chat del Capítulo 3 para hacer vida
más simple. Describamos lo que es necesario para sincronizar a su cliente con Pusher.com.
Primero, debe incluir la biblioteca Pusher.com en su código HTML:
<script src = "http://js.pusher.com/2.1/pusher.min.js" > </script>
Dentro de su código JavaScript, inicializa su objeto Pusher con la clave de aplicación proporcionada
en el panel de Pusher y suscríbete inmediatamente al canal de chat :
var pusher = new Pusher ( 'YOUR-APP-KEY' );
var canal = empujador . suscribirse ( 'chat' );
var id ;
empujador . conexión . bind ( 'conectado' , función () {
$ . ajax ({
url : 'http: // localhost: 8181 / login' ,
tipo : 'POST' ,
dataType : 'json' ,
contentType : "aplicación / json" ,
complete : function ( xhr , status ) {
if ( xhr . estado === 200 ) {
consola . log ( "inicio de sesión exitoso" );
}
},
éxito : función ( resultado ) {
appendLog ( '*' , resultado . apodo + "conectado" );
id = resultado . id ;
}
})
});
empujador . conexión . bind ( 'desconectado' , función () {
appendLog ( '*' , 'Conexión cerrada' );
});
76 | Capítulo 5: Compatibilidad con WebSocket
www.it-ebooks.info

Página 91
función desconectar () {
empujador . desconectar ();
}
canal . bind ( 'mensaje' , función ( datos ) {
appendLog ( datos . apodo , datos . mensaje );
});
canal . bind ( 'apodo' , función ( datos ) {
appendLog ( '*' , de datos . mensaje );
});
El objeto de conexión Pusher emitirá varios eventos y solo le preocupa
con conectado y desconectado . Después de suscribirse al canal de chat , enlaza
a dos eventos específicos en ese canal: mensaje y apodo . Para cada uno de estos,
mostrará mensajes de notificación en la interfaz del cliente. Cuando te unes a y
recibe el evento conectado , envía su solicitud de inicio de sesión a la API del servidor y
Reciba su identificación de cliente para ser pasada en mensajes subsecuentes. La figura 5-1 es una
ejemplo de la aplicación de chat usando Pusher.com.
Figura 5-1. Ejemplo de chat Pusher
Ha visto un ejemplo concreto de cómo utilizar los conceptos básicos de la API disponible para usted. los
La intención es mostrar lo que es posible con alternativas a WebSocket, y Pusher es
definitivamente digno de ser considerado como una alternativa al WebSocket puro.
Pusher.com | 77
www.it-ebooks.info

Página 92
No se olvide: Pusher es una solución comercial
A diferencia de WebSocket, Socket.IO y SocksJS, este marco es un servicio comercial.
La evaluación de la solución y los beneficios que proporciona debe realizarla su equipo.
En general, los diferentes niveles de precios se basan en conexiones, mensajes y
si la conexión está protegida mediante cifrado SSL o no. Para mayor evaluación
ción, revise la página de precios de Pusher .

Proxy inverso
Una de las cosas que probablemente también se le pedirá que haga es proxy de la conexión WebSocket
detrás de un servidor web. Los dos servidores web más comunes son nginx y Apache . los
La configuración para estos es bastante simple, con nginx que tiene la funcionalidad incorporada en el
servidor en sí, y Apache usando un módulo llamado proxy_wstunnel . En lugar de entrar
un montón de detalles sobre cómo configurar ambos servidores para proxy las conexiones,
aquí hay dos artículos de blog que los discuten:
• nginx
• apache

Resumen
Este capítulo presentó tres formas populares de aprovechar el poder de la comunicación bidireccional
comunicación mientras se trata de una API de nivel superior. Estas soluciones te dan el poder
de WebSocket en caso de que su cliente esté utilizando un navegador de cliente moderno y no
volver a Flash socket u otras soluciones menos optimizadas para clientes mayores. Adicionalmente,
los dos últimos marcos agregan características que no son compatibles de forma nativa con WebSocket,
limitar la cantidad de código que tendrá que escribir para admitir la comunicación de sus aplicaciones
comunicación. El siguiente capítulo analiza los métodos para proteger su computadora WebSocket.
comunicación.
78 | Capítulo 5: Compatibilidad con WebSocket
www.it-ebooks.info

Página 93
CAPÍTULO 6
Seguridad de WebSocket
Este capítulo detalla el aparato de seguridad de WebSocket y las diversas formas en que puede
utilícelo para proteger los datos que se pasan a través del protocolo subyacente. Aprenderás por qué
Siempre es una buena idea comunicarse a través de TLS (Transport Layer Security) para evitar
proxies ineficaces y ataques man-in-the-middle, y garantizan la entrega de tramas. Dis‐
La discusión se centra en la configuración de la conexión WebSocket sobre TLS con wss: // (Web‐
Socket Secure), seguridad basada en el origen, enmascaramiento de tramas y límites específicos
impuestos por
navegadores para garantizar que los mensajes no sean secuestrados.
Al igual que con cualquier discusión sobre seguridad, el contenido de este capítulo presenta la
los datos más conocidos sobre cómo proteger adecuadamente su comunicación WebSocket. Seguridad
es voluble, sin embargo, y el juego del gato y el ratón que se juega con aquellos que buscan explotar
y los que trabajan para bloquear es constante e interminable. Validación de datos y múltiples
las comprobaciones son aún más importantes al utilizar WebSocket. Comenzarás configurando
WebSocket sobre TLS.

TLS y WebSocket
Todas las demostraciones hasta ahora han utilizado la versión no cifrada de la comunicación WebSocket
ción con la cadena de conexión ws: // . En la práctica, esto debería suceder solo en
jerarquías más simples, y toda la comunicación a través de WebSocket debe ocurrir a través de TLS.
Generación de un certificado autofirmado
No se puede realizar una conexión válida basada en TLS a través de WebSocket sin un certificado válido
cate. Lo que repasaré bastante rápido aquí es una forma de generar un certificado autofirmado
utilizando OpenSSL. Lo primero que debe hacer es asegurarse de que, si aún no lo ha hecho
tiene OpenSSL instalado, siga el conjunto de instrucciones que se presentan a continuación y que es
cific a su plataforma.
79
www.it-ebooks.info

Página 94
Instalación en Windows
Esta sección cubre solo la descarga e instalación del binario precompilado disponible
en Windows. Como discutimos en el Capítulo 1 , para los masoquistas entre nosotros, usted puede
descargue la fuente y compílela usted mismo.
Para el resto de nosotros, descargue el ejecutable independiente de Windows. Deberías poder
para ejecutar OpenSSL después de esto a través de los ejemplos siguiendo las instrucciones en OS X y
Instalaciones de Linux.
Instalación en OS X
El método más fácil de instalar OpenSSL en OS X es a través de un administrador de paquetes como
Homebrew. Esto permite una actualización rápida y sencilla sin tener que volver a descargar un
paquete de la Web. Suponiendo que tiene Homebrew instalado:
brew instalar openssl

Instalación en Linux
Hay tantos tipos de Linux que sería imposible ilustrar cómo
instalar en todos ellos. Reiteraré cómo instalarlo a través de apt en Ubuntu . Si estás corriendo
En otra distribución, puede leer las instrucciones de compilación e instalación
ciones de OpenSSL.
El uso de apt para la instalación requiere unos simples pasos:
sudo apt-get update
sudo apt-get install openssl
Configuración de WebSocket sobre TLS
Ahora que tiene OpenSSL instalado, puede usarlo para generar un
certificado que se utilizará para realizar pruebas o para enviarlo a una autoridad de certificación.
Una autoridad de certificación (CA) emite certificados digitales en una clave pública
infraestructura (PKI). La CA es una entidad de confianza que certifica la
propiedad de una clave pública como el sujeto designado del certificado.
El certificado se puede utilizar para validar esa propiedad y cifrar
toda la información que pasa por el cable.
Esto es lo que va a hacer en el siguiente bloque de código:
• Genere una clave de 2048 bits con una frase de contraseña
• Vuelva a escribir esa clave eliminando la frase de contraseña
• Cree una solicitud de firma de certificado (CSR) a partir de esa clave
80 | Capítulo 6: Seguridad de WebSocket
www.it-ebooks.info

Página 95
• Genere un certificado autofirmado a partir de la clave y CSR
Lo primero que debe hacer es generar una clave de 2048 bits. Esto se hace mediante el openssl com-
mand para generar el par de claves RSA:
% openssl genrsa -des3 -passout pass: x -out server.pass.key 2048
Generación de clave privada RSA, módulo de 2048 bits de largo
.................................................. .............................
+++ ........ +++
e es 65537 ( 0x10001 )
A continuación, genera una clave privada sin frase de contraseña para la eventual creación de una CSR,
que se puede utilizar para un certificado autofirmado, o para recibir un certificado autorizado por
una autoridad certificadora. Después de generar la clave, puede quitar la clave con la contraseña
frase también:
% openssl rsa -passin pass: x -in server.pass.key -out server.key
escribir clave RSA
% rm server.pass.key
Ahora que tiene su clave privada, puede usarla para crear una CSR que se usará
para generar el certificado autofirmado para enviar comunicaciones WebSocket seguras:
% openssl req -new -key server.key -out server.csr
-subj '/ C = US / ST = California / L = Los Ángeles / O = Mystic Coders, LLC /
OU = Tecnología de la información / CN = ws.mysticcoders.com /
emailAddress = fakeemail AT gmail DOT com /
subjectAltName = DNS.1 = endpoint.com ' > server.csr
Si está buscando configurarse con un certificado de servidor adecuado, el archivo CSR es todo lo que
necesita.
necesitar. Recibirá un archivo de certificado de la autoridad de certificación, que puede
luego use. Sin embargo, mientras espera, obtengamos el certificado autofirmado para probar y
reemplácelo más tarde.
Use el siguiente código para generar su certificado para usar en el código del servidor:
% openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
Firma ok
sujeto = / C = US / ST = California / L = Los Ángeles / ...
Obtener clave privada
En el directorio en el que ha elegido ejecutar todo, ahora debería tener tres archivos:
• server.key
• server.csr
• server.crt
Si decide enviar cosas a una autoridad de certificación para obtener un certificado validado,
envíe el archivo server.csr junto con el procedimiento de configuración para recibir una clave. Porque
eres
TLS y WebSocket | 81
www.it-ebooks.info

Página 96
solo va a usar un certificado autofirmado aquí para fines de prueba, continuará
con su certificado generado server.crt . Decide dónde guardarás la clave privada
y archivos de certificado (en este caso, los colocará en / etc / ssl / certs ).
Ejemplo de servidor WebSocket sobre TLS
En el siguiente código, verá un ejemplo del uso del módulo https para permitir el
comunicación WebSocket bidireccional a través de TLS y escucha en el puerto 8080:
var fs = require ( 'fs' );
// probablemente cargará la configuración desde config
var cfg = {
ssl : cierto ,
puerto : 8080 ,
ssl_key : '/etc/ssl/certs/server.key' ,
ssl_cert : '/etc/ssl/certs/server.crt'
};
var httpsServ = require ( 'https' );
var WebSocket = require ( 'ws' );
var WebSocketServer = WebSocket . Servidor ;
var app = null ;
// procesamiento de solicitudes ficticias
var processRequest = function ( req , res ) {
res . writeHead ( 200 );
res . end ( "¡Hola! \ n" );
};
aplicación = httpsServ . createServer ({
clave : fs . readFileSync ( cfg . ssl_key ),
cert : fs . readFileSync ( cfg . ssl_cert )
}, processRequest ). escuchar ( cfg . puerto );
var wss = new WebSocketServer ({ servidor : aplicación });
wss . on ( 'conexión' , función ( wsConnect ) {
wsConnect . on ( 'mensaje' , función ( mensaje ) {
consola . log ( mensaje );
});
});
82 | Capítulo 6: Seguridad de WebSocket
www.it-ebooks.info

Página 97
Cambiar el código del cliente para usar una conexión WebSocket a través de TLS es trivial:
var ws = new WebSocket ( "wss: // localhost: 8080" );
Al realizar esta conexión, la página web que se utiliza para cargarla también debe conectarse
sobre TLS. De hecho, si intenta cargar una conexión WebSocket insegura desde un
sitio web que utiliza el protocolo https , arrojará un error de seguridad en la mayoría de los
navegadores para intentar cargar contenido inseguro. El contenido mixto es un ataque común
vector y está legítimamente desanimado de que se permita. En la mayoría de los navegadores modernos,
el uso de contenido mixto no solo se desaconseja activamente, sino que está prohibido. Cromo,
Firefox e Internet Explorer arrojan errores de seguridad y se negarán a comunicarse
sobre cualquier otra cosa que no sea WebSocket Secure en caso de que la página que se está cargando sea
también se sirve a través de TLS. Safari, desafortunadamente, no hace lo correcto. La figura 6-1 es
un ejemplo de Chrome que muestra los errores en la consola al intentar conectarse
a un servidor WebSocket inseguro.
Figura 6-1. Error de seguridad de contenido mixto con Chrome
Qualys Labs tiene un buen gráfico que identifica los navegadores que manejan contenido mixto
correctamente, y los que no.
Ahora que su conexión está encriptada, profundizaremos en otros métodos para asegurar la
canal de comunicaciones en la siguiente sección.

Modelo de seguridad basado en el origen


Siempre ha habido una carrera entre quienes buscan explotar las vulnerabilidades en un
mecanismo de transporte y quienes buscan protegerlo. El protocolo WebSocket no es
excepción. Cuando XMLHttpRequest (XHR) apareció por primera vez con Internet Explorer, fue
limitado a la política del mismo origen (SOP) para todas las solicitudes al servidor. Hay innumerables
muchas formas en que esto puede ser explotado, pero funcionó bastante bien. Como el uso de
Sin embargo, XHR evolucionó y se volvió necesario permitir el acceso a otros dominios. Cruzar
Origin Resource Sharing (CORS) fue el resultado de este esfuerzo; si se usa correctamente, CORS
puede minimizar los ataques de secuencias de comandos entre sitios y, al mismo tiempo, permitir
flexibilidad.
CORS, o intercambio de recursos de origen cruzado, es un método de acceso
control empleado por el navegador, generalmente para solicitudes Ajax de un
dominio fuera del dominio de origen. Para mayor información
sobre CORS, consulte el Documentos de Mozilla .
Modelo de seguridad basado en el origen | 83
www.it-ebooks.info

Página 98
WebSocket no impone ninguna restricción de política del mismo origen para acceder a WebSocket
servidores. Tampoco emplea CORS. Lo que te queda con respecto a la validación de origen
dación es la verificación del lado del servidor. Todos los ejemplos anteriores utilizaron el método simple
y
biblioteca ws rápida con Node.js. Continuará haciéndolo y verá en la inicialización cómo
simple es emplear una verificación de origen para garantizar que la conexión desde el navegador sea solo
el Origen esperado :
var WebSocketServer = require ( 'ws' ). servidor ,
wss = new WebSocketServer ({
puerto : 8181 ,
origen : 'http://mydomain.com' ,
verifyClient : function ( info , callback ) {
if ( info . origen === 'http://midominio.com' ) {
devolución de llamada ( verdadero );
volver ;
}
devolución de llamada ( falso );
}
}),
Si escribe una función verifyClient para la biblioteca que está usando, puede enviar una llamada
de vuelta con verdadero o falso, lo que indica una validación exitosa de cualquier información,
incluido el encabezado Origin . Si tiene éxito, verá un HTTP actualizado válido
intercambio por el origen http://mydomain.com.
El intercambio HTTP que ocurre como resultado es el siguiente:
OBTENER ws: //midominio.com/ HTTP / 1.1
Origen: http://mydomain.com
Anfitrión: http://mydomain.com
Sec-WebSocket-Key: zy6Dy9mSAIM7GJZNf9rI1A ==
Actualización: websocket
Conexión: actualización
Sec-WebSocket-Versión: 13
HTTP / 1.1 101 protocolos de conmutación
Conexión: actualización
Sec-WebSocket-Accept: EDJa7WCAQQzMCYNJM42Syuo9SqQ =
Actualización: websocket
Si el encabezado de Origin no coincide, la biblioteca ws devolverá un 401 Unauthor‐
encabezado ized. La conexión nunca completará el protocolo de enlace y no se pueden
enviado de ida y vuelta. Si esto sucede, recibirá una respuesta similar a la siguiente:
HTTP / 1.1 401 no autorizado
Contenido - tipo : texto / html
También debe tenerse en cuenta que verificar el encabezado de origen no constituye una
y conexión autorizada por un cliente válido. Podrías pasar fácilmente el
Encabezado de origen de un script que se ejecuta fuera del navegador. El encabezado Origin puede ser spoo‐
84 | Capítulo 6: Seguridad de WebSocket
www.it-ebooks.info

Página 99
alimentado con casi ningún esfuerzo fuera del navegador. Por lo tanto, estrategias adicionales
debe emplearse para garantizar que su conexión esté autorizada.
El principal beneficio de requerir el encabezado Origin es combatir Cross-like tipo WebSocket.
Ataques de falsificación de solicitud de sitio (CSRF), también llamados secuestro de WebSocket entre
sitios
(CSWSH) , de ser posible ya que el agente de usuario pasa el encabezado Origin , y
no se puede modificar con código JavaScript. La confianza implícita, por tanto, va hacia el cliente
navegador en este caso, y las restricciones que impone al código basado en web.
Clickjacking
Otra área de preocupación con WebSocket y la Web en general se denomina clickjack‐
En g. El proceso implica enmarcar el sitio web solicitado por el cliente y ejecutar el código en
el marco oculto sin que el usuario se dé cuenta.
Para combatir esto, los desarrolladores web han ideado métodos llamados framebusting para garantizar
que el sitio web que visitan sus usuarios no está enmarcado de ninguna manera.
Una forma simple e ingenua de salir de un marco es la siguiente:
if ( top . location ! = location ) {
arriba . ubicación = yo . ubicación ;
}
Sin embargo, esto tiende a fallar debido a inconsistencias en cómo los navegadores han manejado
estas propiedades con JavaScript en el pasado. Otros problemas que surgen están disponibles
funcionalidad de JavaScript en el sistema, o posiblemente en el iframe, que se puede restringir en
ciertos navegadores.
La técnica de framebusting basada en JavaScript más completa disponible, que viene
de un estudio realizado por Stanford Web Security Research sobre framebusting, se describe en
el siguiente fragmento:
< estilo >
cuerpo { pantalla : ninguno ; }
< / estilo>
< guión >
if ( self === top ) {
documentos . getElementsByTagName ( "cuerpo" ) [ 0 ]. estilo . display = 'bloque' ;
} más {
arriba . ubicación = yo . ubicación ;
}
< / script>
De todas las soluciones basadas en JavaScript, esto le permite evitar que el usuario vea
su página si se está enmarcando. La página también permanecerá en blanco si se activa JavaScript
off o se intenta cualquier otra forma de explotar el código de fractura de tramas. Porque Web‐
Socket está basado en JavaScript, romper cualquier marco eliminará cualquier habilidad para un atacante
Modelo de seguridad basado en el origen | 85
www.it-ebooks.info

Página 100
secuestrar el navegador y ejecutar código sin el conocimiento de los usuarios. A continuación mirarás
en un enfoque basado en encabezados que se puede utilizar junto con el script anterior,
y una prueba de concepto llamada Waldo, que aprovecha este vector de ataque.
El uso de las técnicas mencionadas aquí hará que el código de Waldo sea discutible.
Opciones de X-Frame para Framebusting
Microsoft introdujo el método más seguro para evitar el secuestro de clics con
Internet Explorer 8 e incluye una opción de encabezado HTTP llamada X-Frame-Options .
La solución se popularizó y se ha vuelto popular entre los principales navegadores, incluidos
Safari, Firefox, Chrome y Opera, y se ha estandarizado oficialmente como RFC
7034 . Sigue siendo la forma más eficaz de romper los marcos. La Tabla 6-1 muestra el
valores aceptables que puede pasar el servidor para garantizar que solo las políticas aceptables
cies se utilizan para enmarcar el sitio web.
Tabla 6-1. Valores aceptables de X-Frame-Options
Valor de encabezado
Descripción del comportamiento
NEGAR
Evita el encuadre del código en absoluto
MISMO ORIGEN
Evita el enmarcado por sitios externos
ALLOW-FROM origin Permite enmarcar solo por el sitio especificado
¿Por qué importa todo esto en lo que respecta a la comunicación WebSocket? Una prueba de con‐
excepto llamado Wally muestra lo simple que puede ser que un fragmento de JavaScript comprometido
controlar e informar los datos a un servidor WebSocket. Estas son algunas de las cosas
Wally es capaz de lograr:
• Enviar cookies o DOM
• Instalar y recuperar resultados de keylogger
• Ejecutar JavaScript personalizado
• Usar en un ataque de denegación de servicio
Todos los navegadores modernos admiten WebSocket, y la única defensa real contra este ataque.
vector es contramedidas anti-encuadre como X-Frame-Options y, en menor medida
extensión, el otro código de destructor de marcos basado en JavaScript revisado anteriormente.
Si desea probar Wally, puede encontrar las instrucciones de instalación en el sitio web. Por favor
tenga en cuenta que las versiones de las bibliotecas de soporte que usa Waldo han avanzado.
Estas son las versiones compatibles para compilar Waldo:
86 | Capítulo 6: Seguridad de WebSocket
www.it-ebooks.info

Página 101
• biblioteca websocketpp WebSocket (versión 0.2.x)
• Boost con la versión 1.47.0 (puede usar el administrador de paquetes)
Después de instalar websocketpp y descargarwaldo.zip , modifica elarchivocommon.mk
con rutas correctas para boost y websocketpp y build. Crea una página HTML simple
que incluye el JavaScript comprometido en un marco oculto y carga el
sitio web en otro marco. Asegúrese de tener la aplicación Waldo C ++ ejecutándose y controle
a voluntad. Waldo es completamente relevante, ya que fue lanzado basado en RFC 6455 y aún
funciona bien en los últimos navegadores.
Para obtener herramientas más detalladas que le permitan utilizar el navegador como vector de ataque,
usando WebSocket para realizar estas pruebas, consulte BeEF, que viene con un
Interfaz RESTful y GUI, y XSSChef, que se instala como Google comprometido
Extensión de Chrome.

Negación de servicio
WebSocket, por su propia naturaleza, abre conexiones y las mantiene abiertas. Un vector de ataque
tor que se ha utilizado comúnmente con servidores web basados en HTTP es abrir cientos
de conexiones y manténgalas abiertas indefinidamente al enviar lentamente datos válidos a
el servidor web para evitar que se agote el tiempo de espera y agotar los subprocesos disponibles
utilizado en el servidor. El término que se le da al ataque es Slowloris., y mientras más asin‐
servidores crónicos como nginx pueden mitigar el efecto, no siempre es completamente
eficaz. Algunas de las mejores prácticas a tener en cuenta para reducir este ataque son las siguientes:
• Agregue una limitación basada en IP para garantizar que las conexiones provengan de una sola fuente
no abruman la cantidad de conexiones disponibles.
• Asegúrese de que las acciones solicitadas por un usuario se generen de forma asincrónica
en el servidor para disminuir el impacto de los clientes conectados.

Enmascaramiento de marcos
El protocolo WebSocket (RFC 6455), que se analiza con mucho más detalle en el Capítulo 8 ,
define una clave de enmascaramiento de 32 bits que se establece mediante el bit MASK en la trama
WebSocket.
La máscara es una clave aleatoria elegida por el cliente, y es una buena práctica que todos los clientes
establezca el bit MASK junto con pasar la clave de enmascaramiento obligatoria. Cada cuadro debe
incluir una clave de enmascaramiento aleatoria del lado del cliente para que se considere válido. La
máscara-
La clave de ing se usa para XOR los datos de carga antes de enviarlos al servidor, y la
La longitud de los datos de la carga útil no se modificará.
Puede que se esté diciendo a sí mismo: "Eso es genial, pero ¿por qué debería importarme esto cuando
estamos hablando de seguridad? " Dos palabras: envenenamiento de caché. La realidad de una aplicación
en
Denegación de servicio | 87
www.it-ebooks.info

Página 102
lo salvaje es que no puede controlar el mal comportamiento de los servidores proxy, y la relativa novedad
La existencia de WebSocket desafortunadamente significa que puede ser un vector de ataque para el mal
cious.
Un documento de 2011 titulado "Hablar con uno mismo para divertirse y obtener ganancias" describía
múltiples
métodos para engañar a un proxy para que entregue el archivo JavaScript del atacante. Enmascaramiento
efecto introduce un poco de variabilidad que se inyecta en cada mensaje de cliente, que
no puede ser aprovechado por el código JavaScript malicioso de un atacante. El enmascaramiento de
datos asegura
que el envenenamiento de la caché es menos probable debido a la variabilidad en los paquetes de datos.
La desventaja del enmascaramiento es que también evita que las herramientas de seguridad identifiquen
patrones
charranes en el tráfico. Desafortunadamente, debido a que WebSocket es todavía un protocolo bastante
nuevo, un
buen número de proxies, firewalls y DLP de red y endpoint (prevención de pérdida de datos
ción) no pueden inspeccionar correctamente los paquetes que se envían a través del cable.
Además, debido a que muchas herramientas no saben cómo inspeccionar adecuadamente WebSocket
marcos, dData se pueden ocultar en banderas reservadas, los desbordamientos de búfer o
subdesbordamientos son
posible, y el código JavaScript malicioso también se puede ocultar en el marco de la máscara.
No confíes en nadie.

Validación de clientes
Hay muchas formas de validar a los clientes que intentan conectarse a su WebSocket
servidor. Debido a restricciones en el navegador para la conexión a través de WebSocket, no hay
capacidad para pasar cualquier encabezado HTTP personalizado durante el protocolo de enlace. Por lo
tanto, los dos
Los métodos más comunes para implementar la autenticación son usar el encabezado básico y usar
autenticación basada en formulario con una cookie establecida. Este ejemplo emplea el último método y
utiliza
un formulario simple de nombre de usuario / contraseña, configurando y leyendo la cookie en su
WebSocket
servidor.
Configurar dependencias e inicios
Debido a que la solución usa claves compartidas a través de una cookie, necesitará obtener algunas
dependencias
cies en su lugar. Las bibliotecas que usará se enumeran aquí con los comandos npm :
% npm install ws
% npm install express
% npm instalar body-parser
% npm instalar cookie
Es probable que tenga la biblioteca ws instalada en su entorno de ejemplos anteriores.
Utilizará express y complementos para analizar datos de formularios y cookies: body-
analizador y cookie . La dependencia restante es fs , que usará para leer su
Archivos de certificado TLS.
De vuelta al código de su servidor, lo primero que debe hacer es configurar sus importaciones
con require :
88 | Capítulo 6: Seguridad de WebSocket
www.it-ebooks.info

Página 103
var fs = require ( 'fs' );
var https = require ( 'https' );
var cookie = require ( 'cookie' );
var bodyParser = require ( 'body-parser' );
var express = require ( 'express' );
var WebSocket = require ( 'ws' );
Ahora que tiene todas las dependencias necesarias en su lugar, configure el certificado autofirmado
tificar e inicializar express y el servidor HTTPS que lo respalda. Puedes usar el mismo
certificado autofirmado que configuró anteriormente en este capítulo:
var WebSocketServer = WebSocket . Servidor ;
var credentials = {
clave : fs . readFileSync ( 'server.key' , 'utf8' ),
cert : fs . readFileSync ( 'servidor.crt' , 'utf8' )};
var app = express ();
aplicación . use ( bodyParser . json ()); // para analizar la aplicación / json
aplicación . use ( bodyParser . urlencoded ({ extendido : verdadero }));
var httpsServer = https . createServer ( credenciales , aplicación );
httpsServer . escucha ( 8443 );

Escuchar solicitudes web


Para que su autenticación basada en formularios funcione, estará mostrando una página de inicio de
sesión y un
página segura, que requerirá que se encuentre una cookie denominada credenciales y se haya
la clave adecuada dentro. Por brevedad, utilizará la combinación de nombre de usuario / contraseña
de prueba / prueba y use una clave predefinida que nunca cambia. En su propio código, cómo
nunca, estos datos deben guardarse en una fuente de datos de su elección que también puede ser
recuperado por el servidor WebSocket. Los métodos stub se utilizarán para mostrar dónde
Insertaría el código de recuperación y almacenamiento en cualquier fuente de datos que decida usar
en su propia aplicación.
A continuación se muestra el código HTML para el ejemplo de inicio de sesión, lo que le va a servir de
su expreso
servidor. Guarde esto en el directorio de su proyecto y asígnele el nombre login.html :
<html>
<cabeza>
<title> Iniciar sesión </title>
</head>
<cuerpo>
<h1> Iniciar sesión </h1>
<form method = "POST" action = "/ login" name = "login" >
Nombre de usuario: <input type = "text" name = "username" />
Contraseña: <input type = "password" name = "password" />
<input type = "submit" value = "Iniciar sesión" />
</form>
Validación de clientes | 89
www.it-ebooks.info

Página 104
</body>
</html>
Escuchará las solicitudes GET y POST en la URL / inicio de sesión y una solicitud GET
for / secure , que comprueba la cookie para garantizar su existencia y redirecciona si
extraviado:
aplicación . get ( '/ login' , function ( req , res ) {
fs . readFile ( './login.html' , function ( err , html ) {
if ( err ) {
tirar err ;
}
res . writeHeader ( 200 , { "Content-Type" : "text / html" });
res . escribir ( html );
res . fin ();
});
});
aplicación . post ( "/ login" , function ( req , res ) {
if ( req . body ! == 'undefined' ) {
key = validateLogin ( req . body [ 'nombre de usuario' ], req . body [ 'contraseña' ]);
if ( clave ) {
res . cookie ( 'credenciales' , clave );
res . redireccionar ( '/ asegurado' );
volver ;
}
}
res . sendStatus ( 401 );
});
var validateLogin = function ( nombre de usuario , contraseña ) {
if ( nombre de usuario == 'prueba' && contraseña == 'prueba' ) {
return '591a86e4-5d9d-4bc6-8b3e-6447cd671190' ;
} más {
devolver nulo ;
}
}
aplicación . get ( '/ secure' , function ( req , res ) {
cookies = cookie . analizar ( req . encabezados [ 'cookie' ]);
if ( ! cookies . hasOwnProperty ( 'credenciales' )
&& cookies [ 'credentials' ] ! == '591a86e4-5d9d-4bc6-8b3e-6447cd671190' ) {
res . redireccionar ( '/ login' );
} más {
fs . readFile ( './secured.html' , function ( err , html ) {
if ( err ) {
tirar err ;
}
res . writeHeader ( 200 , { "Content-Type" : "text / html" });
res . escribir ( html );
90 | Capítulo 6: Seguridad de WebSocket
www.it-ebooks.info

Página 105
res . fin ();
});
}
});
Como puede ver, ha eliminado un método validateLogin , que en este sencillo
la implementación solo verifica para asegurarse de que el nombre de usuario y la contraseña sean
de prueba .
Después de una validación exitosa, devuelve la clave . En una implementación de producción,
Optaría por almacenar esta clave en un almacén de datos, que luego se puede recuperar y
validado con el extremo del servidor WebSocket. Estamos haciendo un poco de trampa para no agregar
nada innecesario
dependencias esenciales de este ejemplo. El HTML proporcionado por el punto final / secure
como sigue:
<html>
<cabeza>
<title> Ejemplo de autenticación de WebSocket </title>
<guion
src = "https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" > </script>
<script type = "text / javascript" >
$ ( función () {
var ws = new WebSocket ( "wss: // localhost: 8443" );
ws . onopen = función ( e ) {
consola . log ( 'Conexión al servidor abierta' );
}
});
</script>
</head>
<cuerpo>
Hola, WebSocket.
</body>
</html>
Ahora tiene todos los puntos finales web que se ofrecen al usuario y un cliente WebSocket
que se conecta de forma segura a través de TLS al puerto 8443.
Servidor WebSocket
Ha manejado el extremo web del espectro y tiene un cliente WebSocket
cargado y listo para enviar mensajes a su punto final de WebSocket. En el mismo
archivo de origen, incluirá el código para activar un servidor WebSocket seguro y usar
verifyClient para verificar su cookie de credenciales . Lo primero que debe hacer es asegurarse
está hablando a través de un canal seguro y, de lo contrario, devuelva falso a la devolución de llamada que
falla
la conexión. Luego, verifique el encabezado de la cookie y llame a la función checkAuth ,
que en el código de producción buscaría la clave en una fuente de datos y validaría que el
el cliente puede acceder a este servicio. Si todo va bien, regrese fiel a la devolución de llamada y
permitir que la conexión continúe:
Validación de clientes | 91
www.it-ebooks.info

Página 106
var checkAuth = function ( clave ) {
tecla de retorno === '591a86e4-5d9d-4bc6-8b3e-6447cd671190' ;
}
var wss = new WebSocketServer ({
servidor : httpsServer ,
verifyClient : function ( info , callback ) {
if ( info . seguro ! == verdadero ) {
devolución de llamada ( falso );
volver ;
}
var parsed_cookie = cookie . analizar ( info . req . encabezados [ 'cookie' ]);
if ( 'credenciales' en parsed_cookie ) {
if ( checkAuth ( parsed_cookie [ 'credenciales' ])) {
devolución de llamada ( verdadero );
volver ;
}
}
devolución de llamada ( falso );
}
});
wss . on ( 'conexión' , función ( wsConnect ) {
wsConnect . on ( 'mensaje' , función ( mensaje ) {
consola . log ( mensaje );
});
});
Como puede ver, esta es una solución de extremo a extremo para validar que un WebSocket
La conexión está autorizada a continuar. Puede agregar otros cheques según sea necesario, según
la aplicación que se está construyendo. Recuerde que el navegador del cliente no puede configurar
encabezados, por lo que las cookies y el encabezado básico son todo lo que tiene. Esto debería dar
usted la estructura que le permite construir esto en sus propias aplicaciones en un
manera segura, lejos de miradas indiscretas.

Resumen
Este capítulo analizó varios vectores de ataque y formas de proteger su WebSocket
solicitud. La conclusión principal debe ser asumir que el cliente no es un
navegador, por lo que no confíe en él.
92 | Capítulo 6: Seguridad de WebSocket
www.it-ebooks.info

Página 107
Tres cosas para recordar:
• Utilice siempre TLS.
• El código del servidor siempre debe verificar el encabezado de origen .
• Verifique la solicitud utilizando un token aleatorio similar al token CSRF para Ajax
peticiones.
Es aún más importante utilizar los elementos discutidos en este capítulo para que la posibilidad
de que alguien se apropie de la conexión WebSocket para otros propósitos nefastos es
muy minimizado. En el próximo capítulo revisaremos varias formas de depurar
WebSocket y midiendo activamente los beneficios de rendimiento sobre un Ajax regular-
solicitud basada.
Resumen | 93
www.it-ebooks.info

Página 108
www.it-ebooks.info

Página 109
CAPÍTULO 7
Depuración y herramientas
Los capítulos anteriores han profundizado en la creación de soluciones para usar Web‐
Enchufe en sus aplicaciones. Mientras se encuentra en el proceso de integrar cualquier tecnología en un
proyecto nuevo o existente, quizás la herramienta más vital es aprender a depurar cuando
las cosas no salen como se planeó originalmente.
En este capítulo, explorará varias áreas del ciclo de vida de WebSocket y las herramientas de revisión.
que pueden ayudarlo en su viaje a través del panorama de WebSocket. Tomemos uno de los
ejemplos anteriores para dar una vuelta, y echar un vistazo a lo que se transmite y cómo
puede utilizar las herramientas para ver lo que sucede debajo del capó.
Un ciclo de vida típico de WebSocket consta de tres áreas principales: el apretón de manos de apertura,
enviar y recibir tramas y el apretón de manos de cierre. Cada uno puede presentar su propio
desafíos. Resumirlos todos aquí sería imposible, pero mostraré algo de metanfetamina.
ods de investigar en caso de que surjan desafíos durante la depuración.

El apretón de manos
Los datos esperados que recibe el servidor de un cliente válido deben incluir varios HTTP
encabezados como Host , Connection , Upgrade , Sec-WebSocket-Key , Sec-WebSocket-
Versión y otros que son opcionales a WebSocket. Proxies y herramientas de seguridad en
Algunas redes corporativas pueden modificar los encabezados antes de que se transmitan al
servidor y es probable que el protocolo de enlace falle. Para realizar pruebas, puede utilizar
OWASP ZAP . ZAP fue diseñado para ayudar a los probadores de penetración a encontrar
vulnerabilidades
en aplicaciones web, y puede utilizarlo para interceptar el apretón de manos utilizando su
romper la funcionalidad y eliminar algunos de los encabezados importantes antes de que el servidor vea
ellos.
A lo largo de este capítulo, utilizará el ejemplo de código de identidad del Capítulo 3 . los
En las siguientes secciones se reproducen ejemplos completos de servidor y cliente.
95
www.it-ebooks.info

Página 110
El servidor
Aquí está el código completo para la parte del servidor de la aplicación de chat de identidad:
var WebSocket = require ( 'ws' );
var WebSocketServer = WebSocket . servidor ,
wss = new WebSocketServer ({ puerto : 8181 });
var uuid = require ( 'node-uuid' );
var clientes = [];
function wsSend ( tipo , client_uuid , nickname , message ) {
para ( var i = 0 ; i < clientes . longitud ; i ++ ) {
var clientSocket = clientes [ i ]. ws ;
if ( clientSocket . readyState === WebSocket . OPEN ) {
clientSocket . enviar ( JSON . stringify ({
"tipo" : tipo ,
"id" : client_uuid ,
"apodo" : apodo ,
"mensaje" : mensaje
}));
}
}
}
var clientIndex = 1 ;
wss . on ( 'error' , function ( e ) {
consola . log ( "tiempo de error" );
});
wss . on ( 'conexión' , función ( ws ) {
var client_uuid = UUID . v4 ();
var nickname = "AnonymousUser" + clientIndex ;
clientIndex + = 1 ;
Clientes . push ({ "id" : client_uuid , "ws" : ws , "nickname" : nickname });
consola . log ( 'cliente [% s] conectado' , client_uuid );
var connect_message = nickname + "se ha conectado" ;
wsSend ( "notificación" , client_uuid , nickname , connect_message );
ws . on ( 'mensaje' , función ( mensaje ) {
if ( mensaje . indexOf ( '/ nick' ) === 0 ) {
var nickname_array = mensaje . dividir ( '' );
if ( nickname_array . length > = 2 ) {
var old_nickname = apodo ;
nickname = nickname_array [ 1 ];
var nickname_message = "Client" + old_nickname + "cambiado a"
+ apodo ;
wsSend ( "nick_update" , client_uuid , nickname , nickname_message );
}
} más {
96 | Capítulo 7: Depuración y herramientas
www.it-ebooks.info

Página 111
wsSend ( "mensaje" , client_uuid , apodo , mensaje );
}
});
ws . on ( 'error' , function ( e ) {
consola . log ( "ocurre un error" );
});
var closeSocket = function ( customMessage ) {
para ( var i = 0 ; i < clientes . longitud ; i ++ ) {
if ( clientes [ i ]. id == client_uuid ) {
var desconectar_mensaje ;
if ( customMessage ) {
disconnect_message = customMessage ;
} más {
mensaje_desconexión = apodo + "se ha desconectado" ;
}
wsSend ( "notificación" , client_uuid , apodo , desconectar_mensaje );
Clientes . empalme ( i , 1 );
}
}
}
ws . en ( 'cerrar' , función () {
consola . log ( "cierre de enchufe" );
closeSocket ();
});
proceso . en ( 'SIGINT' , function () {
consola . log ( "Cerrando cosas" );
closeSocket ( 'El servidor se ha desconectado' );
proceso . salir ();
});
});

El cliente
Aquí está el código completo para la parte del cliente de la aplicación de chat de identidad:
<! DOCTYPE html>
<html lang = "en" >
<cabeza>
<title> Demostración bidireccional de WebSocket Chat </title>
<meta charset = "utf-8" >
<meta http-equiv = "X-UA-Compatible" content = "IE = edge" >
<meta name = "viewport" content = "width = device-width, initial-scale = 1" >
<link rel = "stylesheet" href = "http://bit.ly/cdn-bootstrap-css" >
<link rel = "stylesheet" href = "http://bit.ly/cdn-bootstrap-theme" >
<script src = "http://bit.ly/cdn-bootstrap-jq" >
</script>
<script>
var ws = new WebSocket ( "ws: // localhost: 8181" );
El apretón de manos | 97
www.it-ebooks.info

Página 112
var nickname = "" ;
ws . onopen = función ( e ) {
consola . log ( 'Conexión al servidor abierta' );
}
function appendLog ( tipo , apodo , mensaje ) {
var mensajes = documento . getElementById ( 'mensajes' );
var messageElem = documento . createElement ( "li" );
var preface_label ;
if ( escriba === 'notificación' ) {
preface_label = "<span class = \" label label-info \ "> * </span>" ;
} else if ( escriba === 'nick_update' ) {
preface_label = "<span class = \" label label-warning \ "> * </span>" ;
} más {
preface_label = "<span class = \" label label-success \ ">" + apodo
+ "</span>" ;
}
var message_text = "<h2>" + preface_label + "& nbsp; & nbsp;" + mensaje
+ "</h2>" ;
messageElem . innerHTML = message_text ;
mensajes . appendChild ( messageElem );
}
ws . onmessage = function ( e ) {
var datos = JSON . analizar ( p . datos );
apodo = datos . apodo ;
appendLog ( datos . tipo , datos . apodo , datos . mensaje );
consola . log ( "ID: [% s] =% s" , datos . ID , datos . mensaje );
}
ws . onclose = function ( e ) {
appendLog ( "Conexión cerrada" );
consola . log ( "Conexión cerrada" );
}
ws . onerror = función ( e ) {
appendLog ( "Error" );
consola . log ( "Error de conexión" );
}
function sendMessage () {
var messageField = documento . getElementById ( 'mensaje' );
if ( ws . readyState === WebSocket . OPEN ) {
ws . enviar ( valor de messageField . );
}
messageField . valor = '' ;
messageField . foco ();
}
función desconectar () {
ws . cerrar ();
}
</script>
</head>
<body lang = "en" >
<div class = "vertical-center" >
98 | Capítulo 7: Depuración y herramientas
www.it-ebooks.info

Página 113
<div class = "contenedor" >
<ul id = "messages" class = "list-unstyled" >
</ul>
<h />
<formulario role = "formulario" id = "chat_form" onsubmit = "sendMessage (); return false;" >
<div class = "form-group" >
<input class = "form-control" type = "text" id = "message" name = "message"
placeholder = "Escriba el texto para hacer eco aquí" value = "" autofocus />
</div>
<button type = "button" id = "enviar" class = "btn btn-primary"
onclick = "sendMessage ();" > Enviar mensaje </button>
</form>
</div>
</div>
<script src = "http://bit.ly/cdn-bootstrap-minjs" > </script>
</body>
</html>

Descargar y configurar ZAP


La mejor manera de seguir la prueba de "servidores proxy incorrectos" es descargar ZAP y ejecutar
para su plataforma específica. ZAP puede actuar como un proxy mientras navega por su
aplicación, por lo que deberá modificar la configuración de red de su navegador. Con ello
muchas iteraciones posibles, es mejor simplemente enlazar con la documentación de ZAP , que habla
sobre una gran cantidad de navegadores y cómo configurar el proxy. El proxy se ejecuta por defecto
en el puerto localhost 8080 y se puede cambiar yendo a Opciones en Herramientas →
Opciones → Proxy local.
En el cliente ZAP, seleccione "Alternar descanso en todas las solicitudes" para que pueda aprobar cada
solicitud antes de que se envíe. Es aquí donde modificará su apretón de manos y
elimine algunos encabezados vitales y necesarios. Al visitar el cliente local abriendo
el archivo client.html , intentará realizar varias conexiones HTTP. Algunos de estos
será para dependencias externas en bootstrap para la interfaz de usuario, y habrá una en marcha
a http: // localhost: 8181 solicitando una conexión de actualización para WebSocket.
Habrá botones Siguiente en el encabezado de la interfaz de usuario que le permitirán pasar por cada
solicitud. Cuando llegue a la solicitud de WebSocket, deténgase y hagamos algunos cambios.
Aquí hay una solicitud similar a la que debería ver en ZAP:
OBTENGA http: // localhost: 8181 / HTTP / 1.1
Anfitrión: localhost: 8181
Conexión: actualización
Pragma: sin caché
Control de caché: sin caché
Actualización: websocket
Origen: nulo
Sec-WebSocket-Versión: 13
Usuario-Agente: Mozilla / 5.0 ( Macintosh ; ...
El apretón de manos | 99
www.it-ebooks.info

Página 114
Aceptar codificación: sdch
Aceptar-Idioma: en-US, en ; q = 0,8, de ; q = 0,6
Sec-WebSocket-Key: BRUZ6wGtxKWln5gToX4MSg ==
Sec-WebSocket-Extensions: permessage-deflate ; client_max_window_bits
En el área de texto, elimine todos los encabezados específicos de WebSocket como un proxy o IDS
incorrecto
podría hacer:
OBTENGA http: // localhost: 8181 / HTTP / 1.1
Anfitrión: localhost: 8181
Conexión: actualización
Pragma: sin caché
Control de caché: sin caché
Origen: nulo
Usuario-Agente: Mozilla / 5.0 ( Macintosh ; Intel Mac OS X 10_10_3 ) AppleWebKit / 537.36 ...
Aceptar codificación: sdch
Aceptar-Idioma: en-US, en ; q = 0,8, de ; q = 0,6
Después de permitir que la solicitud continúe sin los encabezados adecuados, verá en el
respuesta de ZAP un código de error HTTP 426. Este código HTTP indica que un
Se requiere actualización y no se proporcionó. Esto puede ocurrir con frecuencia cuando
interactuar con servidores proxy incorrectos, que analizaremos resolver en la sección “WebSocket
Secure to the Rescue ”en la página 102.
La Figura 7-1 muestra el protocolo de enlace de WebSocket desde la aplicación OWASP.
Figura 7-1. El proxy de ataque OWASP Zed rompe el protocolo de enlace de WebSocket
100 | Capítulo 7: Depuración y herramientas
www.it-ebooks.info

Página 115
Veamos las herramientas para desarrolladores de Chrome; deberían contarnos una historia similar. Puedes
tiene que actualizar la solicitud y volver a pasar con ZAP después de navegar a la
Pestaña Red en la sección Herramientas para desarrolladores de Chrome. Es posible que deba hacer clic
en el filtro
y luego especifique WebSockets . Después de volver a enviarlo, también debería ver el
El código de respuesta HTTP 426 se devuelve al navegador.
La Figura 7-2 muestra el resultado de faltar algunos encabezados en las herramientas de desarrollo de
Chrome.
Figura 7-2. Apretón de manos en las herramientas para desarrolladores de Chrome
¿Qué pasaría si no eliminara todos los encabezados, solo algo que puede ser
importante, como Sec-WebSocket-Version ? Cuando llegue la solicitud, elimine el
encabezado que verá para Sec-WebSocket-Version :
OBTENGA http: // localhost: 8181 / HTTP / 1.1
Anfitrión: localhost: 8181
Conexión: actualización
Pragma: sin caché
Control de caché: sin caché
Actualización: websocket
Origen: nulo
Usuario-Agente: Mozilla / 5.0 ( Macintosh ; Intel Mac OS X 10_10_3 ) AppleWebKit / 537.36 ...
Aceptar codificación: sdch
Aceptar-Idioma: en-US, en ; q = 0,8, de ; q = 0,6
Sec-WebSocket-Key: BRUZ6wGtxKWln5gToX4MSg ==
Sec-WebSocket-Extensions: permessage-deflate ; client_max_window_bits
El apretón de manos | 101
www.it-ebooks.info

Página 116
Después de enviar esta solicitud al servidor, lo que probablemente regresará es una
HTTP 400 Solicitud incorrecta. Falta información vital ( Sec-WebSocket-
Versión ), y no te permitirá continuar. ¿Cómo puede asegurarse de que haya
¿Más posibilidades de que sus mensajes se reciban y envíen correctamente?

WebSocket Secure to the Rescue


Gracias a algunas herramientas ingeniosas, puede ver lo que sucede con su conexión
y por qué las cosas se ven un poco raras. ¿Cómo evitas cosas como los proxies?
¿O herramientas IDS jugando con sus preciosos encabezados? WebSocket Secure es la respuesta.
Como discutimos en el Capítulo 6 , la mejor manera de asegurarse de que su comunicación
llegar a su destino previsto es utilizar siempre wss: // . Si necesita ayuda para configurarlo,
consulte el Capítulo 6 para obtener instrucciones. En general, usando el canal seguro de WebSocket
puede aliviar los problemas descritos en la sección anterior.

Validación del apretón de manos


La mayoría de las bibliotecas y todos los navegadores compatibles con WebSocket RFC 6455
implementarán la
proceso simple de apretón de manos sin falta. Como veremos en el Capítulo 8, la Sec-
WebSocket-Key es un nonce aleatorio codificado en base64 y enviado en la inicial
apretón de manos del cliente. Para validar que su servidor está devolviendo el
valor correcto para el cliente, puede tomar el código de ejemplo en el Capítulo 8 y escribir un
script simple que acepta un Sec-WebSocket-Key y escupe una respuesta adecuada:
var crypto = require ( 'crypto' );
var SPEC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ;
var webSocketAccept = function ( secWebsocketKey ) {
var sha1 = cripto . createHash ( "sha1" );
sha1 . actualizar ( secWebsocketKey + SPEC_GUID , "ascii" );
return sha1 . digest ( "base64" );
}
if ( proceso . argv . longitud <= 2 ) {
consola . log ( "Debe proporcionar una clave Sec-WebSocket-Key como único parámetro" );
proceso . salida ( 1 );
}
var webSocketKey = proceso . argv [ 2 ];
webSocketAccept = webSocketAccept ( webSocketKey );
consola . log ( "Sec-WebSocket-Accept:" , webSocketAccept );
102 | Capítulo 7: Depuración y herramientas
www.it-ebooks.info

Página 117
Si ve otros valores al usar Wireshark o Chrome Developer Tools
(valores que obviamente serían rechazados por el cliente), ejecute este script contra la clave
primero, y luego ver cómo arreglar cualquier error en su servidor. Podria
indican que algo a lo largo del camino de la comunicación se está insertando en la comunicación
comunicación y el protocolo está haciendo lo correcto al rechazarlo.

Inspección de marcos
En más de una ocasión, se le asignará la tarea de averiguar por qué un cliente
recibir datos inesperados procedentes de su servidor. La primera reacción a esto
puede ser agregar algunos registros de depuración a su aplicación y pedirle al cliente que haga otro
intento. Sin embargo, si su código está en producción, no sería aconsejable porque
podría afectar a otros usuarios y podría afectar el rendimiento o la disponibilidad. Otra opción
es utilizar un rastreador de red para observar la comunicación desde el servidor al afectado
cliente. Usemos nuestro ejemplo de chat existente para ver qué pasa por el cable.
Cargas útiles enmascaradas
La mejor manera de ver que cada cuadro cruza el cable es utilizar nuestra confiable herramienta Wire‐
tiburón. Así es, niños, Wireshark no es solo para rastrear conexiones de red en un
¡cafetería! La versátil herramienta de red funciona bien en todas las plataformas y le permite filtrar
e inspeccione el apretón de manos junto con cada fotograma individual enviado a través del cable. A
partir de
la versión 1.9 también se ejecuta sin necesidad de la dependencia X11, que definitivamente es una
prima.
Comenzar con Wireshark es bastante sencillo. Después de descargar con éxito
Al instalar e instalar la herramienta para su plataforma específica, será recibido con la
, que enumera todas las interfaces de red que Wireshark está listo para escuchar.
La Figura 7-3 muestra la pantalla inicial de la herramienta Wireshark.
Haga doble clic en la interfaz en la que buscará estas pruebas y Wireshark
diligentemente comenzará a mostrarle los paquetes capturados en la siguiente pantalla (consulte la Figura
7-4).
Muestra cada paquete capturado en una tabla con columnas ordenables y una barra de filtro en el
superior para facilitar la visualización exacta de lo que busca en el enorme flujo de datos
de ida y vuelta.
Puede optar por seguir una secuencia TCP, UDP o SSL para ver la comunicación bidireccional
la notificación se envía a lo largo del cable. Verá el apretón de manos en la Figura 7-5
y verá una solicitud al servidor del cliente con la carga útil después de HTTP
encabezamiento.
Inspección de marcos | 103
www.it-ebooks.info

Página 118
Figura 7-3. Pantalla principal de Wireshark
Figura 7-4. Pantalla de captura de Wireshark
104 | Capítulo 7: Depuración y herramientas
www.it-ebooks.info

Página 119
Figura 7-5. Cliente de marco de Wireshark al servidor
Discutimos brevemente el enmascaramiento de marcos en Capítulo 6y si estás mirando la carga útil
pensando que no se parece al JSON que esperabas, tienes razón. Ser - estar
considerado válido, todos los clientes deben enmascarar la carga útil con la clave de enmascaramiento de
32 bits
pasado dentro del marco antes de enviar cualquier mensaje a un servidor. Puedes estar diciendo a
usted mismo, “Esto apesta; ¿Cómo se supone que debo depurar efectivamente lo que está pasando con
este cliente con estas vistas anticuadas en mis datos? " No temas, las últimas versiones
de Wireshark admiten el desenmascaramiento automático de solicitudes de WebSocket al servidor.
La Figura 7-6 muestra la clave de enmascaramiento capturada con Wireshark.
He aquí cómo verlo en Wireshark:
1. Busque un marco para seleccionar que tenga [MÁSCARA] en la columna Información.
2. Asegúrese de que Detalles del paquete esté activado y visible.
3. Expanda la sección inferior denominada "WebSocket".
4. Expanda la última sección denominada "Desenmascarar carga útil" y contemple su carga útil.
sin enmascaramiento.
Inspección de marcos | 105
www.it-ebooks.info

Página 120
Figura 7-6. Marco enmascarado WebSocket mostrado en Wireshark
La figura 7-7 muestra un ejemplo de la interfaz de usuario de Wireshark y una muestra de carga útil sin
máscara.
junto con el enmascarado justo encima si lo necesita. Esto es increíblemente poderoso
para depurar las interacciones con varios clientes y su código de servidor.
Ahora puede ver cómo se ve la carga útil desenmascarada para un mensaje específico pasado
de cliente a servidor sin recurrir a la depuración a stderr / stdout o de otra manera
obstaculizando el rendimiento o la disponibilidad en su aplicación. Al buscar opciones,
Chrome Developer Tools también es un valioso compañero de nuestros esfuerzos, aunque lo hace
requieren que usted sea el cliente y pueda replicar los errores de su entorno.
Una de las razones por las que he encontrado que Wireshark es tan poderoso en este sentido es la
capacidad de ver la transmisión sin modificar o interrumpir a otros clientes en el
proceso.
106 | Capítulo 7: Depuración y herramientas
www.it-ebooks.info

Página 121
Figura 7-7. Carga útil desenmascarada de WebSocket mostrada en Wireshark
Aunque es interesante ver cargas útiles sin máscara específicas, otras veces es más
apropiado para ver el hilo de conversación completo para un cliente específico. Para esto,
Wireshark también es indispensable. Mientras sigue capturando su comunicación WebSocket
cation, puede hacer clic derecho o Comando-clic en cualquiera de los WebSocket o la inicial
Apretón de manos HTTP en la comunicación y elija Seguir → Seguir secuencia TCP.
En el momento de la captura, le mostrará la conversación completa para ese
cliente.
La Figura 7-8 muestra el menú contextual necesario para seguir el flujo de TCP.
Mientras observa la conversación que se muestra en la ventana Follow TCP Stream win‐
dow, volviendo a la pantalla de captura principal, debería notar que la captura
aparece filtrado. Siempre que sigas una transmisión, la seleccionará y la filtrará.
cualquier otra cosa para que pueda concentrarse. En la siguiente sección se explica cómo depurar y ver
para fotogramas cercanos utilizando Wireshark.
Inspección de marcos | 107
www.it-ebooks.info
Página 122
Figura 7-8. Wireshark sigue la secuencia TCP

Conexión de cierre
Lo último que verá en una conversación de WebSocket es el marco de cierre. Decir
que agrega un botón a su interfaz de usuario, lo que permite al cliente enviar un marco cercano al
servidor así:
<button type = "button" onclick = "desconectar ();" > Desconectar </button>
A continuación, podría utilizar algunas de las cosas que aprendió sobre la visualización de fotogramas en
Wire‐
tiburón. Como corresponde a nuestra tarea, sería ver el marco enmascarado enviado por el cliente.
y mirando el mensaje muy pequeño con la carga útil vacía para una solicitud de cierre. Si
ve el marco y abre la sección WebSocket desde Wireshark,
debería ver algo similar a lo siguiente:
WebSocket
1 ... .... = Fin : Verdadero
. 000 .... = Reservado : 0x00
.... 1000 = Código de operación : Conexión cerrada ( 8 )
1 ... .... = Máscara : Verdadero
. 000 0000 = Longitud de carga útil : 0
Enmascaramiento - Clave : 4021 df19
Los códigos de estado registrados para un cierre por motivos específicos de RFC se definen en el capítulo
ter 8. El encabezado anterior indica que ocurrió un cierre normal y no hay mensaje
fue aprobada. Si desea pasar su propio código de estado al cierre y / o un mensaje
108 | Capítulo 7: Depuración y herramientas
www.it-ebooks.info

Página 123
sabio, puede hacerlo usando la API de JavaScript como discutimos en el Capítulo 2 con el
siguiendo:
ws . close ( 1000 , 'Finalizado normalmente' );
La carga útil sería idéntica a cómo se reciben los mensajes normalmente, incluyendo
enmascarado y enviado junto con los encabezados en el marco. El código de operación adornaría
el código pasado en la llamada de JavaScript, la máscara se establecería y la clave de enmascaramiento
se utilizaría para enmascarar el mensaje que se envía al cliente. Esa es la comunicación final
información que recibirá con una conversación de WebSocket, y ha aprendido a
mírelo todo y modifique las cosas según sea necesario para probar diferentes escenarios.

Resumen
Este capítulo cubrió el apretón de manos de apertura, los marcos y el apretón de manos de cierre.
y presentó herramientas que le permiten observar la interacción entre el cliente y
servidor. A continuación, se presenta un resumen de algunos de los problemas que puede identificar
utilizando estos
herramientas.
Si recibe un código que no sea HTTP 200 durante el apretón de manos de apertura:
• Es probable que esté involucrado un proxy o IDS incorrecto y que elimine encabezados. El código
podría
indicar que no usó WebSocket Secure, que es preferible a usar el
conexión regular no TLS.
• El código también podría indicar que algo salió mal con la Sec-
WebSocket-Key o Sec-WebSocket-Accept que se envió en respuesta. Usted puede
Esté atento a la solicitud y la respuesta utilizando OWASP, Chrome Developer
Tools, o Wireshark y ejecute los valores contra el script que escribió en "Validat‐
apretón de manos ”en la página 102 .
Si un cliente se queja de errores pero no tiene visibilidad debido al enmascaramiento:
• Utilice Wireshark y siga las instrucciones que detallan cómo ver el servidor
respuestas y solicitudes hechas por el cliente para que pueda comprender completamente dónde
las cosas van mal.
Y finalmente, si se está produciendo un cierre:
• Utilice cualquiera de las herramientas enumeradas para ver lo que el cliente envía o responde
desde el servidor, vea el código y / o mensaje, y maneje en consecuencia.
Pudimos identificar algunos obstáculos potenciales a lo largo del camino y cómo podrían
identificados usando herramientas para que no terminen en una búsqueda de días durante la depuración
proceso de ging. Las herramientas deben servir como compañeros dignos durante el desarrollo
Resumen | 109
www.it-ebooks.info

Página 124
proceso y durante la depuración cuando la aplicación entra en producción y usted
Necesito una mejor visión de lo que está sucediendo.
Puede notar que el método de depuración a través del navegador se centró exclusivamente en
Herramientas para desarrolladores de Chrome. Safari no ofrece una forma discernible de depurar
WebSocket
marcos. Firefox le mostrará el apretón de manos de apertura y todos los encabezados asociados con
la conexión, pero no hay inspección del marco disponible. Según error de Mozilla
885508 , todavía está abierto y parece que no hay implementación disponible en el
maravillosas herramientas de desarrollo. Sin embargo, tienes muchas herramientas a tu
disposición; Cromo
junto con Wireshark y OWASP ZAP pueden brindarle la introspección que necesita para
averigüe qué está pasando cuando las cosas van mal.
El capítulo final presenta una mirada más profunda al protocolo WebSocket en sí.
110 | Capítulo 7: Depuración y herramientas
www.it-ebooks.info

Página 125
CAPÍTULO 8
Protocolo WebSocket
Sin discusión sobre los protocolos, especialmente los que se inician a través de una llamada HTTP,
estaría completo sin hablar un poco sobre la historia de HTTP. El inicio de
WebSocket surgió debido a la popularidad masiva de Ajax y en tiempo real
actualizaciones. Con HTTP, un protocolo donde un cliente solicita un recurso y el servidor
responde con el recurso o posiblemente un código de error si algo salió mal. Esta
La naturaleza unidireccional se ha solucionado utilizando tecnologías como Comet y
sondeo largo, pero tiene un costo de recursos informáticos en el lado del servidor. Web-
Socket busca ser una de las técnicas que resuelve este problema y permite web
desarrolladores para implementar la comunicación bidireccional sobre la misma solicitud HTTP.

HTTP 0.9: Nace la Web


El nacimiento de la World Wide Web dio lugar a las primeras versiones del hipertexto
Protocolo de transferencia (HTTP). La primera versión de HTTP fue inventada por Tim.
Berners-Lee junto con el lenguaje de marcado de hipertexto (HTML). HTTP
0.9 fue increíblemente simple. Un cliente solicita contenido a través del método GET :
OBTENER /index.html
La simplicidad de HTTP 0.9 significaba que solo podía solicitar dos cosas: texto sin formato
o HTML. Esta versión inicial de HTTP no tenía encabezados, por lo que no era posible
servir a cualquier medio. En esencia, como cliente solicitó un recurso del servidor
utilizando TCP, y una vez que el servidor terminó de enviarlo, se cerró la conexión.

HTTP 1.0 y 1.1


La simplicidad en 0.9 no iba a durar mucho. Con la próxima versión de HTTP, el
La complejidad involucrada en un par de solicitud / respuesta HTTP aumentó. Las últimas versiones de
HTTP agregó la capacidad de enviar encabezados HTTP con cada solicitud. Con ese crecimiento
111
www.it-ebooks.info

Página 126
número de encabezados para admitir, cosas como solicitudes POST (formulario), tipos de medios,
el almacenamiento en caché y la autenticación se agregaron en HTTP 1.0. En la última versión, multi‐
servidores alojados con el encabezado del host, negociación de contenido, conexiones persistentes y
Se agregaron respuestas fragmentadas y se utilizan en servidores de producción en la actualidad. El punto
de todo esto es que a medida que HTTP ha aumentado en complejidad, el tamaño de los encabezados ha
aumentado.
Según un documento técnico de Google que habla de SPDY, el encabezado HTTP promedio es
ahora 800 bytes y, a menudo, hasta 2 KB. La compresión y otras técnicas son
fácilmente disponible para simplificar esta situación. A continuación se muestra un HTTP típico
encabezado del popular motor de búsqueda Google:
% curl : http : //www.google.com
HTTP / 1.1 200 OK
Fecha : miércoles , 20 de mayo de 2015 22 : 50 : 00 GMT
Caduca : - 1
Caché - Control : privado , máximo - edad = 0
Contenido - Tipo : texto / html ; juego de caracteres = ISO - 8859 - 1
Establecer - Cookie : PREF = ID = 68769 f4bb498a69f : FF = 0 : T ...
Establecer - Cookie : NID = 67 = D26hM_BKWVnngC - 7 _1 - XGmBR ...
P3P : CP = "¡Esta no es una política de P3P! Ver http ..."
Servidor : gws
X - XSS - Protección : 1 ; modo = bloquear
X - Marco - Opciones : SAMEORIGIN
Alternativo - Protocolo : 80 : quic , p = 0
Transferencia - Codificación : fragmentado
Aceptar - Rangos : ninguno
Variar : Aceptar - Codificación
Me tomé la libertad de eliminar el contenido del encabezado de la cookie y dejé cuántos
caracteres que tomó en el encabezado. En total, el encabezado tenía 850 caracteres, o
poco menos de 1 KB. Cuando busca enviar datos de un servidor a otro
cliente y viceversa, tener que enviar un encabezado de 1 KB además es innecesario y
antieconómico. Como verá, después del protocolo de enlace inicial, un encabezado de marco de
WebSocket es mínimo
iscule en comparación y similar a abrir una conexión TCP a través de HTTP.
Las siguientes secciones contienen ejemplos de código que muestran cómo construir partes de
el protocolo del servidor. Tomados en conjunto, puede construir su propia implementación de
un servidor WebSocket compatible con RFC.

Apretón de manos abierto de WebSocket


Uno de los muchos beneficios del protocolo WebSocket es que comienza su conexión a
el servidor como una simple solicitud HTTP. Navegadores y clientes compatibles con WebSocket
envíe al servidor una solicitud con encabezados específicos que soliciten una conexión: actualice a
utilice WebSocket. El encabezado Connection: Upgrade se introdujo en HTTP / 1.1 para
Permitir al cliente notificar al servidor de medios alternativos de comunicación. Es pri‐
112 | Capítulo 8: Protocolo WebSocket
www.it-ebooks.info

Página 127
utilizado en este punto como un medio de actualizar HTTP para utilizar WebSocket y puede ser
utilizado para actualizar a HTTP / 2.
De acuerdo con la especificación WebSocket, la única indicación de que una conexión a la Web
El servidor de socket ha sido aceptado es el campo de encabezado Sec-WebSocket-Accept . El valor
es un hash de un GUID predefinido y el encabezado HTTP del cliente Sec-WebSocket-Key .
De RFC 6455
El campo de encabezado Sec-WebSocket-Accept indica si el
El servidor está dispuesto a aceptar la conexión. Si está presente, este encabezado
El campo debe incluir un hash del nonce del cliente enviado en Sec-
WebSocket-Key junto con un GUID predefinido. Cualquier otro valor
no debe interpretarse como una aceptación de la conexión por parte del
servidor.
Sec-WebSocket-Key y Sec-WebSocket-Accept
Lo primero que pide la especificación en el lado del cliente para generar el Sec-WebSocket-
La clave es un valor aleatorio único o único. Si está utilizando un navegador que admita
WebSocket, la generación de la clave Sec-WebSocket-Key se realizará automáticamente por
utilizando la API de JavaScript. Una de las restricciones de seguridad es que XMLHttpRequest
no se le permitirá modificar ese encabezado. Como comentamos en el Capítulo 6, esto garantiza
que incluso si el sitio web está comprometido, puede confiar en que el navegador no permitirá
cualquier encabezado que se va a modificar.
Generando la clave Sec-WebSocket
El siguiente código asumirá que se ejecuta en Node.js y posiblemente use WebSocket
para comunicarse con otro servicio que actúa como servidor WebSocket. Usarás un
GUID generado mediante el módulo node-uuid , que debería resultar aleatorio
suficiente para sus necesidades.
Lo único que debe hacer en este punto es basar 64 en su nonce e incluirlo
en los encabezados HTTP para su solicitud de conexión WebSocket. Utilizará el nodo
Módulo uuid requerido anteriormente para crear su cadena aleatoria:
var uuid = require ( 'node-uuid' );
var webSocketKey = function () {
var wsUUID = UUID . v1 ();
devolver nuevo búfer ( wsUUID ). toString ( 'base64' );
}
Apretón de manos abierto de WebSocket | 113
www.it-ebooks.info

Página 128
Responder con Sec-WebSocket-Accept
En el lado del servidor, lo primero que hará es incluir el módulo criptográfico para que pueda
envíe su hash SHA1 del valor combinado:
var crypto = require ( 'crypto' );
RFC 6455 define un GUID predefinido, que definirá como una constante en su código:
var SPEC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ;
Su siguiente tarea es definir una función en su código JavaScript que acepte la Sec-
WebSocket-Key como parámetro y crea un objeto hash criptográfico SHA1:
var webSocketAccept = function ( secWebsocketKey ) {
var sha1 = cripto . createHash ( "sha1" );
Finalmente, agregará Sec-WebSocket-Key junto con el GUID predefinido,
pasando eso a su objeto hash SHA1. La actualización de la función actualizará el hash
contenido con sus datos combinados. Se pasa en ASCII para identificar la codificación de entrada
para la actualización SHA1:
sha1 . actualizar ( secWebsocketKey + SPEC_GUID , "ascii" );
return sha1 . digest ( "base64" );
Generar el encabezado Sec-WebSocket-Accept suele ser el trabajo de una biblioteca de servidor.
Es una buena idea comprender el funcionamiento interno y tener una forma de probar si alguien
la cosa debería salir mal.
Encabezados HTTP de WebSocket
La conexión WebSocket debe ser una solicitud GET HTTP / 1.1 e incluir lo siguiente
encabezados bajos:
• Anfitrión
• Actualización: websocket
• Conexión: actualización
• Sec-WebSocket-Key
• Versión Sec-WebSocket
Si alguno de estos no está incluido en los encabezados HTTP, el servidor debe responder con
un código de error HTTP 400 Solicitud incorrecta . Aquí hay un ejemplo de una solicitud HTTP simple
para actualizar para WebSocket. La disposición de los encabezados no es tan importante como
su existencia:
114 | Capítulo 8: Protocolo WebSocket
www.it-ebooks.info

Página 129
OBTENGA ws : // localhost: 8181 / HTTP / 1.1
Origen : http : // localhost: 8181
Anfitrión : localhost : 8181
Sec - WebSocket - Clave : zy6Dy9mSAIM7GJZNf9rI1A ==
Actualización : websocket
Conexión : actualización
Sec - WebSocket - Versión : 13
La Tabla 8-1 muestra los posibles encabezados en el apretón de manos de apertura.
Tabla 8-1. Abrir encabezados de apretón de manos
Encabezamiento
Necesario
Valor
Anfitrión
si
Campo de encabezado que contiene la autoridad del servidor.
Potenciar
si
websocket
Conexión
si
Potenciar
Segundo-
WebSocket-Key
si
Campo de encabezado con un valor codificado en base64 que, cuando se decodifica, tiene una longitud de 16 bytes.
Segundo-
WebSocket-
Versión
si
13
Origen
No
Opcionalmente, un campo de encabezado de origen . Este campo de encabezado lo envían todos los clientes del
navegador. UNA
El intento de conexión que carece de este campo de encabezado no debe interpretarse como proveniente de un
cliente de navegador. Enviar el dominio de origen en la actualización es para que las conexiones puedan ser
restringido para prevenir ataques CSRF similares a CORS para XMLHttpRequest .
Segundo-
WebSocket-
Aceptar
Sí (servidor) El servidor devuelve una confirmación que se describe después de la tabla y debe
presente para que la conexión sea válida.
Segundo-
WebSocket-
Protocolo
No
Opcionalmente, un campo de encabezado Sec-WebSocket-Protocol , con una lista de valores
indicando qué protocolos le gustaría hablar al cliente, ordenados por preferencia.
Segundo-
WebSocket-
Extensiones
No
Opcionalmente, un campo de encabezado Sec-WebSocket-Extensions , con una lista de valores
indicando qué extensiones le gustaría hablar al cliente. La interpretación de este
El campo de encabezado se discute en RFC 6455 Sección 9.1.
Al recibir una solicitud de actualización válida con todos los campos obligatorios, el servidor decidirá
en el protocolo aceptado y cualquier extensión, y envíe una respuesta HTTP con
código de estado 101 junto con el reconocimiento del protocolo de enlace Sec-WebSocket-Accept .
Apretón de manos abierto de WebSocket | 115
www.it-ebooks.info

Página 130
El siguiente código muestra una respuesta simple del servidor aceptando la Web‐
Solicitud de socket y apertura del canal para comunicarse usando WebSocket:
HTTP / 1.1 101 protocolos de conmutación
Conexión : actualización
Sec - WebSocket - Aceptar : EDJa7WCAQQzMCYNJM42Syuo9SqQ =
Actualización : websocket
A continuación, repasaremos el encabezado del marco de WebSocket en detalle, a nivel de bits porque el
el protocolo es binario y no de texto.

Marco WebSocket
Un mensaje de WebSocket se compone de uno o más marcos. La trama es una sin‐
impuesto que contiene la siguiente información, cada una de las cuales describiré en
mayor detalle. Como recordará del Capítulo 2 , los detalles del marco, frag
Mentación y enmascaramiento están protegidos y se mantienen en la implementación de bajo nivel.
detalle del lado del servidor y del cliente. Sin embargo, definitivamente es bueno entender
porque depurar WebSocket con esta información hace que las cosas sean mucho más potentes
más lleno que sin él.
Poco de aleta
¿Es este el cuadro final o hay una continuación?
Código de operación
¿Es este un marco de comando o un marco de datos?
Longitud
¿Cuánto dura la carga útil?
Longitud extendida
Si la carga útil es mayor que 125, usaremos los siguientes 2 a 8 bytes.
Máscara
¿Este marco está enmascarado?
Clave de enmascaramiento
4 bytes para la clave de enmascaramiento.
Datos de carga útil
Los datos a enviar, ya sean binarios o UTF-8, pueden ser una combinación de
datos de extensión + datos de carga útil.
Un mensaje de WebSocket puede componer varios marcos dependiendo de cómo el servidor
y el cliente decide enviar datos de un lado a otro. Y porque la comunicacion
entre cliente y servidor es bidireccional, en cualquier momento que cualquiera de las partes decida, los
datos pueden ser
enviado de un lado a otro siempre que ningún lado haya enviado previamente un marco cercano. los
A continuación se muestra una representación de texto de un marco WebSocket:
116 | Capítulo 8: Protocolo WebSocket
www.it-ebooks.info

Página 131
0
1
2
3
01234567890123456789012345678901
+ - + - + - + - + ------- + - + ------------- + ----------------- -------------- +
| F | R | R | R | opcode | M | Carga útil len |
Longitud de carga útil extendida
|
| Yo | S | S | S | ( 4 ) | A |
(7)
|
( 16 / 64 )
|
|N|V|V|V|
|S|
|
( Si la carga útil len == 126 / 127 )
|
||1|2|3|
|K|
|
|
+ - + - + - + - + ------- + - + ------------- + - - - - - - - - - - - - - - - +
|
La longitud de la carga útil extendida continúa , si la carga útil len == 127 |
+ - - - - - - - - - - - - - - - + ------------------------------- +
|
| Enmascaramiento - tecla , si MASK se establece en 1 |
+ ------------------------------- + ----------------- -------------- +
| Enmascaramiento - clave ( continuación )
|
Datos de carga útil
|
+ -------------------------------- - - - - - - - - - - - - - - +
:
Datos de carga útil, continuación ...
:
+-----------------------------+
|
Datos de carga útil, continuación ...
|
+ ------------------------------------------------- -------------- +
Hablemos de cada uno de los elementos del encabezado con mayor detalle.
Poco de aleta
El primer bit del encabezado de WebSocket es el bit Fin. Si el bit está establecido, este fragmento es
la parte final de un mensaje. Si el bit está claro, el mensaje no está completo con lo siguiente
fragmento de mugido. Como verá en la siguiente sección, el código de operación para pasar es 0x00 .
Códigos de operación de marco
Cada cuadro tiene un código de operación que identifica lo que representa el cuadro. Estos códigos de
operación
se definen en RFC 6455 . Los valores iniciales son los definidos por IANA en la Web.
Registro de sockets y actualmente en uso; adiciones a esto son posibles con WebSocket
Extensiones. El código de operación se coloca dentro de los segundos 4 bits del primer byte del
encabezado del marco. La Tabla 8-2 enumera las definiciones de códigos de operación.
Tabla 8-2. Definición de código de operación
Valor del código de operación Descripción
0x00
Marco de continuación; esta trama continúa la carga útil de la anterior.
0x01
Marco de texto; este marco incluye datos de texto UTF-8.
0x02
Marco binario; este marco incluye datos binarios.
0x08
Conexión Cerrar marco; esta trama termina la conexión.
0x09
Marco de ping; este marco es un ping.
Marco WebSocket | 117
www.it-ebooks.info

Página 132
Valor del código de operación Descripción
0x0a
Marco de Pong; este marco es un pong.
0x0b-0x0f Reservado para futuras tramas de control.

Enmascaramiento
De forma predeterminada, todos los marcos de WebSocket deben enmascararse desde el extremo del
cliente y el servidor
se supone que cierra la conexión si recibe una trama que indique lo contrario. Como tu
descubierto en "Enmascaramiento de marcos" en la página 87, el enmascaramiento introduce variación en
el
marco para evitar el envenenamiento de la caché. El segundo byte de la trama lo ocupa el
longitud en los últimos 7 bits, y el primer bit indica si la trama está enmascarada. los
La máscara a aplicar serán los 4 bytes siguientes a la longitud extendida del WebSocket.
encabezado del marco. Todos los mensajes recibidos por un servidor WebSocket deben desenmascararse
antes
su posterior procesamiento:
var unmask = function ( máscara , búfer ) {
var payload = new Buffer ( buffer . length );
for ( var i = 0 ; i < buffer . length ; i ++ ) {
carga útil [ i ] = máscara [ i % 4 ] ^ búfer [ i ];
}
devolver la carga útil ;
}
Después del desenmascaramiento, el servidor puede decodificar UTF-8 para mensajes basados en texto
(código de operación
0x01 ) y entregar sin cambios para mensajes binarios (código de operación 0x02 ).
Longitud
La longitud de la carga útil está definida por los últimos 7 bits del segundo byte del encabezado de la
trama.
El primer byte es el código de operación definido anteriormente. Dependiendo de cuánto tiempo termine
la carga útil
siendo, puede o no usar los bytes de longitud extendida que siguen a los primeros 2
bytes de encabezado:
• Para mensajes de menos de 126 bytes (0-125), la longitud se empaqueta en los últimos 7 bits de
el segundo byte del encabezado de la trama.
• Para mensajes entre 126 y 216, se utilizan dos bytes adicionales en la extensión
longitud siguiendo la longitud inicial. Se colocará un valor de 126 dentro de los primeros 7
bits de la sección de longitud para indicar el uso de los siguientes 2 bytes de longitud.
• Para mensajes mayores de 216, terminará usando los 8 bytes completos que siguen a la
longitud. Se colocará un valor de 127 dentro de los primeros 7 bits de la sección de longitud para
indica el uso de los siguientes 8 bytes de longitud.
118 | Capítulo 8: Protocolo WebSocket
www.it-ebooks.info

Página 133
Fragmentación
En dos casos, dividir un mensaje en varios marcos podría tener sentido.
Un caso es que sin la capacidad de fragmentar los mensajes, el punto final tendría que
almacena en búfer el mensaje completo antes de enviarlo para que pueda enviar un recuento preciso.
Con la capacidad de fragmentar, el punto final puede elegir un búfer de tamaño razonable y
cuando esté lleno, envíe otro cuadro como continuación hasta que todo esté completo.
El segundo caso es la multiplexación, en la que no es deseable llenar la tubería con datos.
que se comparte y, en su lugar, se divide en varios fragmentos antes de enviarlo. Multi-
plexing no se admite directamente en el protocolo WebSocket, pero la extensión x-
google-mux puede ofrecer soporte. Para obtener más información sobre las extensiones y cómo se
relacionan con
el protocolo WebSocket, echa un vistazo “Extensiones de WebSocket” en la página 122 .
Si una trama no está fragmentada, el bit Fin se establece y contiene un código de operación diferente a
0x00 . Si está fragmentado, se debe utilizar el mismo código de operación al enviar cada trama hasta
el mensaje se ha completado. Además, el bit Fin sería 0x00 hasta el final
frame, que estaría vacío aparte del conjunto de bits Fin y se usaría un código de operación de 0x00 .
Si envía un mensaje fragmentado, debe haber la capacidad de intercalar el control
tramas cuando cualquiera de los lados está aceptando la comunicación (si se envió un mensaje grande y
un marco de control no se pudo enviar hasta el final, sería bastante ineficiente). los
Lo último que es necesario recordar es que el mensaje fragmentado debe ser todo el
mismo tipo: sin mezcla ni coincidencia de datos de cadenas binarios y UTF-8 en un solo
mensaje.

Apretón de manos de WebSocket Close


El protocolo de enlace de cierre para una conexión WebSocket requiere que se envíe una trama con
el código de operación de 0x08 . Si el cliente envía el marco de cierre, se debe enmascarar como se hace
en
todos los demás casos del cliente, y no enmascarados al volver del servidor. Además de-
al código de operación, el marco cerrado puede contener un cuerpo que indica una razón para
cierre, en forma de código y mensaje. El código de estado se pasa en el cuerpo de
el mensaje y es un entero sin signo de 2 bytes. El resto de la razón por la que la cadena seguiría
low, y al igual que con los mensajes WebSocket, sería una cadena codificada en UTF-8.
La Tabla 8-3 muestra los códigos de estado disponibles para un evento de cierre de WebSocket . Cada una
de las
Los códigos de estado registrados en el RFC se identifican y describen en la siguiente sección.
Apretón de manos de WebSocket Close | 119
www.it-ebooks.info

Página 134
Tabla 8-3. Códigos de estado registrados de WebSocket
Estado
código
Sentido
Descripción
1000
Cierre normal
Envíe este código cuando su solicitud se haya completado con éxito.
1001
Va a desaparecer
Envíe este código cuando el servidor o la aplicación cliente se esté apagando o cerrando
sin expectativa de continuar.
1002
Error de protocolo
Envíe este código cuando la conexión se cierre con un error de protocolo.
1003
Datos no admitidos
Envíe este código cuando su aplicación haya recibido un mensaje de un tipo inesperado que
no puede manejar.
1004
Reservado
No utilice; esto está reservado según RFC 6455.
1005
Sin estado rcvd
No utilice; la API usará esto para indicar cuando no se recibió un código válido.
1006
Cierre anormal
No utilice; la API utilizará esto para indicar que la conexión se ha cerrado de forma anormal.
1007
Marco no válido
datos de carga útil
Envíe este código si los datos del mensaje recibido no coinciden con el tipo de
mensaje (por ejemplo, no UTF-8).
1008
Violación de la política
Envíe este código cuando el mensaje recibido haya infringido una política. Este es un estado genérico
código que se puede devolver cuando no hay códigos de estado más adecuados.
1009
Mensaje demasiado grande
Envíe este código cuando el mensaje recibido sea demasiado grande para procesarlo.
1010
Ext. Obligatorio
Envíe este código si espera una extensión del servidor pero no se devolvió en
el protocolo de enlace de WebSocket.
1011
Error interno
Envíe este código cuando la conexión se interrumpa debido a una condición inesperada.
1012
Reinicio del servicio
Envíe este código indicando que el servicio se reinicia y un cliente que se reconecta debe
hágalo con un retraso aleatorio de 5 a 30 segundos.
1013
Vuelve a intentarlo más tarde
Envíe este código cuando el servidor esté sobrecargado y el cliente deba conectarse a un
IP diferente (dados múltiples objetivos), o reconectarse a la misma IP cuando el usuario ha realizado
una acción.
1014
Sin asignar
No utilice; esto no está asignado, pero podría cambiarse en futuras revisiones.
1015
Apretón de manos TLS
No utilice; esto se envía cuando falla el protocolo de enlace TLS.
A diferencia de TCP, donde las conexiones se pueden cerrar en cualquier momento sin previo aviso, la
Web
El cierre del enchufe es un apretón de manos en ambos lados. El RFC también identifica los rangos y
120 | Capítulo 8: Protocolo WebSocket
www.it-ebooks.info

Página 135
lo que significan categóricamente para su aplicación. En general, usará el
rango definido para la versión actual ( 1000 a 1013 ), y dado cualquier
códigos necesarios en su aplicación, el rango no registrado 4000 - 4999 está disponible.
Si un punto final recibe un marco de cierre sin enviar uno, debe enviar un cierre
frame como respuesta (repitiendo el código de estado recibido). Además, no más datos
puede pasar a través de una conexión WebSocket a la que se le ha enviado un marco Cerrar previamente.
Ciertamente, hay casos en los que un punto final retrasa el envío de una trama cerrada hasta que
se envía su mensaje actual (en el caso de mensajes fragmentados), pero la probabilidad
el otro extremo procesaría ese mensaje no está garantizado.
Cuando un punto final (cliente o servidor) ha enviado y recibido un marco de cierre, la Web
La conexión de socket está cerrada y la conexión TCP debe estar cerrada. Un servidor
siempre cierre la conexión después de recibir y enviar inmediatamente, mientras que el cliente
debe esperar a que se cierre un servidor, o configurar un tiempo de espera para cerrar la configuración de
TCP subyacente
conexión en un período de tiempo razonable después de un fotograma cerrado.
La IANA tiene un registro de los códigos de estado de WebSocket para usar durante el cierre.
apretón de manos.
La Tabla 8-4 muestra la gama completa de códigos de estado para un evento de cierre de WebSocket .
Tabla 8-4. Intervalos de códigos de cierre de WebSocket
Rango de estado Descripción
0–999
Este rango no se usa para códigos de estado.
1000–2999 Los códigos de estado en este rango están definidos por RFC 6455 o estarán en revisiones futuras.
3000–3999 Este rango está reservado para bibliotecas, marcos y aplicaciones.
4000–4999 Este rango está reservado para uso privado y no está registrado en la IANA. No dude en utilizar estos valores en
su
código entre cliente y servidor con acuerdo previo.

Subprotocolos de WebSocket
El RFC para WebSocket define subprotocolos y negociación de protocolo entre cli‐
ent y servidor. En el Capítulo 2 , vio cómo pasar uno o más protocolos a través del
API de JavaScript WebSocket. Ahora que estamos en el capítulo dedicado a las entrañas de
WebSocket, puede ver cómo ocurre realmente esa negociación, o no. En el
nivel más bajo, la negociación de qué protocolo usar para una conexión WebSocket
sucede a través del encabezado HTTP Sec-WebSocket-Protocol . Este encabezado se pasa en
con la solicitud de actualización inicial enviada por el cliente:
Sec-WebSocket-Protocol: com.acme.chat, com.acme.anotherchat
Subprotocolos de WebSocket | 121
www.it-ebooks.info

Página 136
En este caso, el cliente le está diciendo al servidor que los dos protocolos que le gustaría
hablar son de chat o anotherchat . En este punto, depende del servidor decidir qué
protocolo que elegirá. Si el servidor no está de acuerdo con ninguno de los protocolos, devolverá
nulo o no devolverá ese encabezado. Si el servidor está de acuerdo con un subprotocolo,
responder con un encabezado como este:
Sec-WebSocket-Protocol: com.acme.anotherchat
Como recordará del Capítulo 2 , su objeto JavaScript WebSocket tendrá
el protocolo de propiedad poblado con el valor elegido por el servidor, o ninguno si
nada fue elegido. En este caso, la API tendrá el valor com.acme.another‐
chatear porque la respuesta del protocolo de enlace del servidor indica que esto es aceptable
protocolo para comunicarse. Un subprotocolo no cambia la Web subyacente
Protocolo de socket, pero simplemente capas encima de él, proporcionando una comunicación de nivel
superior
canal de configuración sobre el protocolo existente. La capacidad de cambiar la definición de un
El marco WebSocket está disponible para usted, sin embargo, en forma de "Extensiones WebSocket"
en la página 122.
Recuerde del Capítulo 2 que se pueden usar tres tipos de subprotocolos con el sub‐
protocolo de protocolo de enlace. Los primeros son los protocolos registrados, identificados en
WebSocket
RFC 6455, sección 11.5. Define un registro con laIANA. Los segundos son pro‐
tocols como XMPP o STOMP, aunque puede ver protocolos registrados para estos
también. Y el tercero, que probablemente usará en su aplicación, son los programas personalizados
tocols, que generalmente toman la forma del nombre de dominio con un identificador para el sub‐
nombre del protocolo.

Extensiones de WebSocket
El WebSocket RFC define Sec-WebSocket-Extensions como un HTTP opcional
encabezado para ser enviado por el cliente de conexión preguntando si el servidor puede soportar
cualquiera de los
extensiones enumeradas. El cliente pasará una o más extensiones con posibles parámetros
ters a través del encabezado HTTP, y el servidor responderá con uno o más aceptados
extensiones. El servidor puede elegir solo de la lista de clientes pasados.
Las extensiones tienen control para agregar nuevos códigos de operación y campos de datos al formato de
encuadre.
En esencia, puede cambiar por completo el formato completo de un marco WebSocket con un
Extensión WebSocket. Una de las especificaciones anteriores, draft-ietf-hybi-
thewebsocketprotocol-10 , incluso mencionó una extensión deflate-stream , que
comprimiría todo el flujo de WebSocket. La efectividad de esto es probablemente
la razón por la que ya no aparece en especificaciones posteriores, porque WebSocket tiene cliente a
enmascaramiento del marco del servidor, mediante el cual la máscara cambia por marco, y con eso,
desinflar
sería totalmente ineficaz.
122 | Capítulo 8: Protocolo WebSocket
www.it-ebooks.info

Página 137
A continuación, se muestran dos ejemplos de extensiones que están disponibles en los clientes en la
actualidad:
• deflate-frame , un mejor método de desinflado (disponible con Chrome, que utiliza x-
webkit-deflate-frame como su nombre) donde los marcos se comprimen en la fuente y
extraído en destino
• x-google-mux, una extensión de etapa inicial que admite multiplexación
La única advertencia, y ha sido un problema con la adopción de cualquier nueva tecnología adjunta.
para los navegadores como clientes, es que el soporte debe integrarse en los navegadores utilizados por su
clientela. El servidor analizará las extensiones pasadas por el cliente y devolverá la
lista que apoyará. El orden de las extensiones devueltas debe coincidir con lo que se
pasado por el cliente. Debe devolver solo las extensiones que el cliente haya indicado
que también admite.

Implementaciones de servidores alternativos


He elegido en este libro centrarme exclusivamente en el uso de Node.js en el lado del servidor.
Las implementaciones del protocolo WebSocket en el lado del servidor están muy extendidas y
cubierto en casi todos los idiomas imaginables. Cubriendo cualquiera de estos otros servidores del lado
Las opciones están ciertamente fuera del alcance de este libro. La siguiente es una no exhaustiva
lista de algunas de las implementaciones compatibles con RFC de WebSocket en la actualidad
para algunos de los idiomas más populares:
• API de Java para WebSocket (JSR-356), que se incluye en cualquier compatibilidad con Java EE 7
servidor ble como Glassfish o Jetty.
• Python tiene varias opciones, dos de las cuales están disponibles en pywebsocket y en
ws4py.
• PHP tiene una implementación compatible con Ratchet
• Ruby tiene una implementación basada en EventMachine, em-websocket.
Estas son solo algunas de las implementaciones más populares en cada idioma. Al igual que con
cualquier decisión técnica en el backend, evalúe las opciones para su plataforma elegida
y utilice estos y la información de este libro como guía a lo largo del camino.
Implementaciones de servidores alternativos | 123
www.it-ebooks.info

Página 138
Resumen
Este capítulo ha profundizado mucho sobre el protocolo WebSocket, con suerte
suficiente para que lo use tal como está o lo extienda en forma de subprotocolos en capas
además del protocolo WebSocket subyacente. El protocolo WebSocket ha tomado un
un largo camino para llegar a donde está hoy, y aunque pueden ocurrir cambios en el futuro,
parece ser una forma sólida de comunicarse de una manera más eficiente y poderosa. Eso
Es hora de acabar con los trucos históricamente necesarios del pasado y abrazar la
potencia proporcionada por el protocolo WebSocket y su API.
124 | Capítulo 8: Protocolo WebSocket
www.it-ebooks.info
Página 139

Índice
UNA
método addEventListener (), 13 ,36
Adobe Flash Socket, 67
Protocolo de cola de mensajes avanzado (AMQP),
44 ,56
implementaciones de servidor alternativo, 123
Apache, 78
API (Application Programming Interface), 9 - 21
atributos, 18- 19
eventos, 12 -dieciséis
inicializando, 9 -11
métodos, 16- 18
Pusher (consulte Pusher.com)
servidor de ejemplo de stock, 19 - 21
IU de ejemplo de stock, 11- 12
pruebas de apoyo, 21
Array.indexOf, 41
atributos
bufferAmount, 19
protocolo, 19
readyState, 17, 18
si
Encabezado básico, 88
chat bidireccional, 23- 34
aplicación de chat básica, 24- 27
código de cliente, 31- 34 , 97- 99
identidad del cliente, 27- 29
eventos y notificaciones, 29- 30
código del servidor, 30 - 31, 96- 97
Cliente WebSocket, 27
Bootstrap (Twitter), 11
soporte de navegador (ver compatibilidad)
prueba de soporte del navegador, 21
atributo bufferedAmount, 19
C
envenenamiento de caché, 87
autoridad certificadora (CA), 80
solicitud de firma de certificado (CSR), 81
canales, Pusher.com, 71- 72
chat (ver chat bidireccional)
clientes de chat
Pusher.com, 76 -77
Socket.IO, 69
SockJS, 66
WebSocket, 31- 34
servidores de chat
Pusher.com, 73 -76
Socket.IO, 68
SockJS, 63 -66
WebSocket, 30- 31
Herramientas para desarrolladores de Chrome, 6, 101 -102, 106
Clickjacking, 85- 86
clientes, validación (ver validación de clientes)
evento cercano, 15 ,26
método cercano, 17 -18
apretón de manos de cierre, 119 -121
atributo de código, 15 ,dieciséis
compatibilidad, 61 - 78
Pusher.com, 70 -78
proxy inverso, 78
Socket.IO, 66 - 70
SockJS, 62 -66
mensajes de conexión / desconexión, 29- 30
Conexión: encabezado de actualización, 112
función connect_callback, 59
encabezado de longitud de contenido, 37
125
www.it-ebooks.info

Página 140
Uso compartido de recursos de origen cruzado (CORS), 83- 84
Ataques de falsificación de solicitudes entre sitios (CSRF), 85
solicitudes entre dominios, 23
Secuestro de WebSocket entre sitios (CSWSH), 85
protocolos personalizados, 10
re
enmascaramiento de datos, 87- 88
depuración, 95- 110
(ver también herramientas)
conexión de cierre, 108 - 109
validación de apretón de manos, 102
inspeccionar marcos, 103- 107
deflate-frame (ver extensiones de WebSocket)
Denegación de servicio (DoS), 87
mi
Servidor de eco, 6
evento de error, 15
eventos, 12 - 16
cerca, 15 ,26
error, 15
mensaje, 14 - 15, 26
abierto, 13
PING PONG, 15
Pusher.com, 72 - 73
Toma.IO, 67- 68
SockJS, 63- 65
Biblioteca express, 64 -66
F
Aleta, 117 , 119
autenticación basada en formulario con cookie, 88- 92
fragmentación, 119
enmascaramiento de marco, 87- 88, 103 - 107 , 118
framebusting, 85 -87
marcos, 116
(ver también el marco de WebSocket)
marco de cierre, 108 - 109
inspeccionando 103- 107
H
apretón de manos, 95 -102
código de cliente, 97- 99
cerca, 108- 109, 119 -121
Encabezados HTTP, 114- 116
abierta, 112 - 116
Sec-WebSocket-Key y Sec-Websocket-
Aceptar, 113- 114
código del servidor, 96 -97
validando, 102
encabezados
Básico, 88
HTTP, 111 ,114- 116
Encabezados HTTP, 111, 114 - 116
Historial HTTP, 111 -112
yo
Chat de retransmisión por Internet (IRC), 28
J
JavaScript, 1, 39
prueba de compatibilidad del navegador, 21
framebusting, 85- 86
jQuery, 11
L
Linux
instalar Node.js y npm, 2
instalar OpenSSL, 80
sondeo largo, 7 - 8 ,23
METRO
enmascaramiento 87 -88
clave de enmascaramiento, 87
evento de mensaje, 14 - 15, 26
métodos
cerca, 17 -18
enviar, 16- 17
multiplexación, 119
norte
rastreadores de red (consulte Wireshark)
nginx, 9, 78 ,87
Node.js, 1- 3
administrador de paquetes (npm), 2- 3, 24 ,44 ,63 ,88
Cliente STOMP para, 57
O
navegadores antiguos (ver compatibilidad)
en el controlador <nombre del evento>, 13
códigos de operación, 117
evento abierto, 13
protocolos abiertos, 10
126 | Índice
www.it-ebooks.info

Página 141
Instalaciones OpenSSL, 80
Modelo de seguridad basado en el origen, 83 -87
clickjacking, 85- 86
Opciones de X-Frame para romper el marco, 86 - 87
OS X
instalando Node.js y npm, 2
instalar OpenSSL, 80
OWASP ZAP, 95, 99- 101
PAGS
longitud de carga útil, 118
Eventos PING / PONG, 15
función process_frame (ver STOMP)
protocolAttribute, 19
protocolos, 10 - 11
(ver también el protocolo WebSocket)
proxy_wstunnel, 78
Pusher.com, 70- 78
canales, 71 - 72
cliente de chat, 76- 77
ejemplo de chat, 77
servidor de chat, 73 -76
eventos, 72 -73
R
RabbitMQ, 35
conectar el servidor a, 44 - 48
configurar, 42 - 44
demonio del precio de las acciones, 47 -48
con Web-Stomp, 56 -59
(ver también Web-Stomp)
Atributo readyState, 17, 18
atributo de razón, 15 , 16
protocolos registrados, 10
proxy inverso, 78
RFC 6455, 1 ,10 ,61 ,87 , 113 - 114 ,117
RFC 7034, 86
S
política del mismo origen (SOP), 83
Sec-WebSocket-Accept, 114
Sec-WebSocket-Key, 113
seguridad, 79- 93
Denegación de servicio (DoS), 87
enmascaramiento de marcos, 87 -88
Modelo de seguridad basado en el origen, 83- 87
TLS (seguridad de la capa de transporte), 79 -83
validación de clientes, 88- 92
enviar funciones (ver STOMP)
método de envío, 16- 17
ID de sesión (ver STOMP)
Loris lento, 87
Toma.IO, 24, 66- 70
Socket de Adobe Flash, 67
transportes alternativos, 66
cliente de chat, 69 -70
servidor de chat, 68
conectando 67- 68
eventos, 67- 68
nombrar en, 68 -70
SockJS, 62- 66
cliente de chat, 66
servidor de chat, 63- 66
manejo de eventos, 63 - 65
biblioteca, 66
bibliotecas de servidor, 62
transcripciones admitidas, 62
STOMP (Simple Text Oriented Messaging Pro‐
tocol), 35 -59
aplicación de cliente, 50 -56
Comandos CONNECT / DISCONNECT, 50 ,
54- 56
conectar el servidor a RabbitMQ, 44 -48
evento de conexión, 40- 41
conexión a través del servidor, 39- 42
encabezado de longitud de contenido, 37
función error_callback, 59
conectarse, 36 - 39
implementación, 36- 42
Estructura de objetos de JavaScript, 39
Comando MENSAJE, 55
archivos necesarios, 35
estructura de objeto, 39
procesamiento de solicitudes STOMP, 49- 50
función process_frame, 39
envío de fotogramas compatibles con STOMP, 37
función send_error, 39
función send_frame, 39
función enviar_mensaje, 59
id-sesión, 37
configuración de Rabbit MQ, 42 -44
Cliente STOMP para Web y Node.js, 57
Biblioteca Stomp.js, 58
stomp_helper.js, 37
Comandos SUBSCRIBE / UNSUBSCRIBE,
50- 54
Web-Stomp, 56 -59
Índice | 127
www.it-ebooks.info

Página 142
subprotocolos, 10 ,35 ,121- 122
(ver también STOMP (Simple Text Oriented
Protocolo de mensajería))
Comandos SUBSCRIBE / UNSUBSCRIBE (consulte
PISAR MUY FUERTE)
T
TLS (seguridad de la capa de transporte), 79 - 83
ejemplo, 82- 83
generar un certificado autofirmado, 79- 82
error de seguridad de contenido mixto, 83
Configuración de WebSocket superior a 80 -83
herramientas
Herramientas para desarrolladores de Chrome, 6, 101 -102, 106
OWASP ZAP, 95 ,99 -101
Vagabundo, 42 - 44
Wireshark, 103- 107
Bootstrap de Twitter, 11
U
UUID (identificador único universal), 24- 25, 50
V
Vagabundo, 42- 44
validar clientes, 88 -92
escuchando solicitudes web, 89 - 91
configurar dependencias e inits, 88- 89
Servidor WebSocket, 91- 92
W
Wally, 86 -87
Web-Stomp, 56- 59
cliente de eco para, 57- 59
instalación, 57
WebSocket
API (consulte API (Programación de aplicaciones
Interfaz))
cerrar rangos de código, 121
parámetros del constructor, 10
eventos, 10
Hola Mundo! ejemplo,3- 7
inicialización, 4- 5
descripción general, 1 -3
códigos de estado registrados, 119
versus encuestas largas, 7- 8
Cliente WebSocket, 27
Extensiones WebSocket, 122- 123
Marco WebSocket, 116- 119
Aleta, 117, 119
fragmentación, 119
longitud, 118
enmascaramiento, 118
códigos de operación, 117
Apretón de manos de apertura / cierre de WebSocket (consulte la
sacudir)
Protocolo WebSocket
extensiones, 122 -123
Historial HTTP, 111- 112
apretón de manos abierto (ver apretón de manos)
subprotocolos, 121 - 122
Marco Websocket, 116 -119
WebSocket seguro, 83 ,102
(ver también TLS (seguridad de la capa de transporte))
Registro de nombres de subprotocolo de WebSocket, 36
cliente web_socket.js, 71
Ventanas
instalar Node.js y npm, 2
instalar OpenSSL, 80
Wireshark, 103- 107
X
Opciones de X-Frame, 86- 87
x-google-mux, 123
XHR (XMLHttpRequest), 23, 83
128 | Índice
www.it-ebooks.info
Página 143
Sobre el Autor
Andrew Lombardi es un empresario y desarrollador de software veterano. Sus padres
le enseñó a codificar, aunque apenas podía leer, en un Apple II que todavía desea
él tuvo. Ha dirigido la consultora Mystic Coders durante 15 años, codificando,
hablando internacionalmente y ofreciendo orientación técnica a empresas tan grandes como
Walmart y empresas con problemas tan interesantes como la simulación de helicópteros. Él
cree firmemente que lo mejor que ha hecho hasta ahora es ser un gran padre.
Colofón
El animal de la portada de WebSocket es una anémona de mar (orden Actiniaria ).
Más de 1.000 especies de anémonas marinas se encuentran en todos los océanos del mundo.
Son particularmente abundantes en las aguas tropicales costeras. Estos organismos tienden a
permanecen arraigados en un solo lugar, anclados a superficies duras como arrecifes de coral o rocas en
el fondo del mar.
Estrechamente relacionadas con los corales y las medusas, las anémonas de mar son invertebrados con
formas cilíndricas.
cuerpos rodeados de tentáculos. Varían en tamaño, desde media pulgada hasta seis
pies de diámetro, y puede poseer desde una docena hasta varios cientos de tenta‐
cles. También aparecen en una variedad de colores vivos, que se asemejan a flores como sus
homónimo, la anémona terrestre.
A pesar de su elegante belleza, estos animales son bastante depredadores. Sus tentáculos son
salpicado de células urticantes que se utilizan para inmovilizar y consumir peces pequeños y
crustáceos. En consecuencia, las anémonas de mar tienen muy pocos depredadores propios, y
muchas especies viven más de 50 años.
Las anémonas de mar se citan con frecuencia por sus relaciones simbióticas con el pez payaso,
que tienen una capa protectora que los hace inmunes a la letalidad de la anémona
picadura. El pez payaso está a salvo de sus enemigos entre los tentáculos de la anémona, mientras
la anémona disfruta de los restos de comida de las comidas del pez payaso.
Muchos de los animales de las portadas de O'Reilly están en peligro; todos ellos son importantes para
el mundo. Para obtener más información sobre cómo puede ayudar, visite animals.oreilly.com .
La imagen de la portada es de placas sueltas (fuente original desconocida). Las fuentes de la portada son
Máquina de escribir URW y Guardian Sans. La fuente del texto es Adobe Minion Pro; El encabezado
la fuente es Adobe Myriad Condensed; y la fuente del código es Ubuntu Mono de Dalton Maag.
www.it-ebooks.info

También podría gustarte