send() y onmessage en WebSockets: Envío y Recepción de Mensajes
Aprende a enviar y recibir mensajes a través de conexiones WebSocket persistentes, incluyendo datos binarios, JSON y manejo de reconexión.
TL;DR - Resumen rápido
- El método send() envía mensajes al servidor WebSocket, aceptando strings, ArrayBuffer, Blob o TypedArray
- El evento onmessage recibe mensajes del servidor con los datos en event.data
- WebSocket soporta tanto datos de texto como binarios de forma nativa
- Usa JSON para mensajes estructurados con múltiples campos y metadatos
- Implementa reconexión automática con backoff exponencial para conexiones perdidas
Introducción al Mensajería WebSocket
Una vez establecida la conexión WebSocket, el núcleo de la comunicación se basa en dos operaciones fundamentales: enviar mensajes al servidor usando el método send() y recibir mensajes del servidor a través del evento message. Esta comunicación es bidireccional y simétrica, lo que significa que tanto el cliente como el servidor pueden enviar mensajes en cualquier momento sin necesidad de esperar una respuesta previa.
La API WebSocket proporciona una interfaz simple pero potente para el intercambio de mensajes. El método send() acepta varios tipos de datos incluyendo strings, ArrayBuffers, Blobs y TypedArrays, lo que permite transmitir tanto datos textuales como binarios de forma eficiente. Por otro lado, el evento message entrega los datos recibidos en la propiedad event.data, manteniendo el tipo de datos original enviado por el servidor.
Ventajas de la mensajería WebSocket
La mensajería WebSocket es eficiente porque elimina el overhead de HTTP headers en cada mensaje, tiene baja latencia al mantener la conexión persistente, soporta datos binarios nativamente sin necesidad de codificación base64, y permite comunicación en tiempo real sin polling. Esto resulta en aplicaciones más responsivas y con menor consumo de ancho de banda.
Enviar Mensajes con send()
El método send() es la forma principal de enviar datos al servidor WebSocket. Este método acepta varios tipos de datos y los transmite de forma eficiente a través de la conexión establecida. Es importante verificar que la conexión esté en estado OPEN antes de llamar a send(), ya que intentar enviar mensajes en otros estados lanzará una excepción.
Este código muestra cómo enviar diferentes tipos de datos usando send(). El método acepta strings directamente, lo cual es ideal para mensajes de texto simples. Para datos binarios, puedes usar ArrayBuffer, Blob o TypedArray como Uint8Array. Cada tipo de dato tiene sus casos de uso específicos: strings para mensajes legibles, ArrayBuffer para datos binarios brutos, Blob para archivos grandes, y TypedArray para datos numéricos estructurados.
- <strong>String</strong>: Para mensajes de texto, comandos o datos JSON serializados.
- <strong>ArrayBuffer</strong>: Para datos binarios brutos como imágenes o audio.
- <strong>Blob</strong>: Para archivos grandes o datos binarios con metadatos.
- <strong>TypedArray</strong>: Para datos numéricos estructurados como coordenadas o mediciones.
Recibir Mensajes con onmessage
El evento message se dispara cada vez que el servidor WebSocket envía datos al cliente. Este evento es la forma principal de recibir información del servidor y proporciona los datos en la propiedad event.data. El tipo de datos recibido depende de lo que envió el servidor, manteniendo la integridad del tipo original (string, ArrayBuffer, Blob o TypedArray).
Puedes escuchar el evento message usando addEventListener('message', handler) o asignando una función a la propiedad onmessage. Ambas formas son funcionalmente equivalentes, pero addEventListener es preferible cuando necesitas múltiples handlers o quieres mayor flexibilidad en el manejo de eventos.
Este código muestra cómo recibir y procesar diferentes tipos de mensajes del servidor. La propiedad event.data contiene los datos enviados por el servidor. Puedes verificar el tipo de datos usando typeof para strings o instanceof para objetos como ArrayBuffer y Blob. Es importante manejar cada tipo de dato apropiadamente: los strings pueden parsearse como JSON si contienen datos estructurados, mientras que los datos binarios pueden procesarse según su formato específico.
Mejor práctica: validar mensajes
Siempre valida los mensajes recibidos antes de procesarlos. Verifica que event.data tenga el formato esperado, que los campos requeridos estén presentes, y que los valores estén dentro de rangos aceptables. Esto previene errores y vulnerabilidades de seguridad, especialmente cuando el servidor no es de confianza o los mensajes pueden ser manipulados.
Datos Binarios
WebSocket soporta transmisión de datos binarios de forma nativa, lo cual es una ventaja significativa sobre otras APIs de comunicación que requieren codificación como base64. Los datos binarios son ideales para transmitir imágenes, audio, video, archivos y cualquier dato que no sea texto. La API WebSocket puede enviar y recibir ArrayBuffer, Blob y TypedArray de forma eficiente.
Para controlar qué tipo de datos binarios recibes, puedes configurar la propiedad binaryType del objeto WebSocket. Los valores posibles son 'blob' (valor por defecto) y 'arraybuffer'. 'blob' es ideal para archivos grandes que no necesitas procesar inmediatamente, mientras que 'arraybuffer' es mejor cuando necesitas acceso directo a los bytes para procesamiento en tiempo real.
Este código muestra cómo enviar y recibir datos binarios usando WebSocket. La propiedad binaryType controla el formato de los datos binarios recibidos. Cuando se establece en 'arraybuffer', los datos binarios se reciben como ArrayBuffer que puedes acceder directamente usando Uint8Array u otras vistas. Cuando se establece en 'blob' (valor por defecto), los datos se reciben como Blob que es ideal para archivos grandes o cuando necesitas metadatos del archivo.
- <strong>binaryType = 'blob'</strong>: Recibe datos como Blob, ideal para archivos grandes y descargas.
- <strong>binaryType = 'arraybuffer'</strong>: Recibe datos como ArrayBuffer, ideal para procesamiento en tiempo real.
- <strong>TypedArray</strong>: Usa Uint8Array, Int32Array, etc., para datos numéricos estructurados.
- <strong>Efficiency</strong>: Los datos binarios no requieren codificación base64, ahorrando ancho de banda.
Optimización: usa binaryType apropiado
Elige 'blob' cuando recibas archivos grandes que no necesitas procesar inmediatamente, ya que Blob puede manejar datos de forma más eficiente en memoria. Usa 'arraybuffer' cuando necesitas acceso directo a los bytes para procesamiento en tiempo real, como en aplicaciones de audio o video streaming.
Mensajes JSON
JSON es el formato más común para mensajes WebSocket estructurados, ya que permite transmitir datos complejos con múltiples campos y tipos de datos en un solo mensaje. Usar JSON facilita la comunicación entre cliente y servidor, especialmente cuando necesitas enviar metadatos junto con los datos o cuando los mensajes tienen estructuras variables.
Para enviar mensajes JSON, primero convierte el objeto JavaScript a string usando JSON.stringify(). Para recibir mensajes JSON, parsea el string usando JSON.parse(). Es importante manejar errores de parsing, ya que un mensaje malformado puede causar que tu aplicación falle. Siempre envuelve JSON.parse() en un bloque try-catch para manejar errores de forma elegante.
Este código muestra cómo enviar y recibir mensajes JSON a través de WebSocket. El objeto mensaje tiene una estructura con campos como type para identificar el tipo de mensaje, payload para los datos principales, y timestamp para metadatos temporales. Al recibir un mensaje, verificamos que sea un string antes de intentar parsearlo como JSON, y usamos un bloque try-catch para manejar errores de parsing de forma segura.
Error común: no validar JSON
Un error frecuente es asumir que todos los mensajes son JSON sin verificar primero. Si el servidor envía un mensaje de texto simple o un mensaje malformado, JSON.parse() lanzará una excepción. Siempre verifica que typeof event.data === 'string' y usa try-catch al parsear JSON para evitar que tu aplicación falle.
Manejo de Reconexión
Las conexiones WebSocket pueden perderse por varias razones: el servidor se reinicia, hay problemas de red, el usuario pierde conexión a internet, o el servidor cierra la conexión por inactividad. Implementar reconexión automática es esencial para aplicaciones en tiempo real que necesitan mantener comunicación continua con el servidor.
Una estrategia efectiva de reconexión usa backoff exponencial: esperar cada vez más tiempo entre intentos de reconexión para evitar sobrecargar el servidor. Comienza con un intervalo corto (por ejemplo, 1 segundo) y duplica el tiempo en cada intento hasta alcanzar un máximo (por ejemplo, 30 segundos). Esto reduce la carga en el servidor cuando hay problemas persistentes de red.
Este código implementa reconexión automática con backoff exponencial. La función connect() establece la conexión WebSocket y maneja el evento close para iniciar la reconexión. El intervalo de reconexión comienza en 1000ms y se duplica en cada intento hasta un máximo de 30000ms (duplicando el tiempo entre intentos para reducir carga en el servidor). La función scheduleReconnect() programa el próximo intento de reconexión, y resetReconnection() reinicia el intervalo cuando la conexión tiene éxito.
Mejor práctica: heartbeat/ping
Implementa heartbeat o ping periódico para detectar conexiones muertas silenciosamente. Envía un mensaje ping cada 30 segundos y espera un pong del servidor. Si no recibes respuesta después de varios intentos, cierra y reconecta. Esto detecta conexiones que parecen abiertas pero no funcionan correctamente.
Resumen: Mensajería WebSocket
Conceptos principales:
- •send() envía mensajes al servidor aceptando strings, ArrayBuffer, Blob y TypedArray
- •onmessage recibe mensajes del servidor con datos en event.data
- •WebSocket soporta datos binarios nativamente sin necesidad de codificación base64
- •JSON es el formato más común para mensajes estructurados con múltiples campos
- •La reconexión automática con backoff exponencial maneja conexiones perdidas
Mejores prácticas:
- •Verifica readyState === WebSocket.OPEN antes de llamar a send()
- •Usa try-catch al parsear JSON para manejar mensajes malformados
- •Elige binaryType apropiado: 'blob' para archivos, 'arraybuffer' para procesamiento en tiempo real
- •Implementa reconexión automática con backoff exponencial y límite máximo
- •Usa heartbeat/ping para detectar conexiones muertas silenciosamente