Bert y Ernie, dos amigos, están trabajando juntos en el proyecto paralelo de sus sueños: una aplicación de chat que permite conversaciones en tiempo real para equipos que trabajan de forma remota. Bert, el programador front-end, apenas puede contenerse mientras comparte nuevas y brillantes funciones con su amigo especialista en back-end. “¡Mira! Puedo escribir un mensaje, se envía al servidor y puedo ver una respuesta que aparece casi al instante”. Ernie, el gurú del back-end, frunce el ceño. “Pero, ¿cómo se resuelve el problema de que dos personas intenten mantener una conversación al mismo tiempo? Tu aplicación sigue actualizando la página en lugar de permitirte 'ver' la conversación, lo que hace que sea imposible chatear en tiempo real”.
El desafío es que la aplicación de Bert opera con HTTP, en el que un cliente debe solicitar una búsqueda del servidor. Ernie resume: “Es como si todos tus amigos te estuvieran respondiendo mensajes de texto, pero de repente no puedes ver ninguna de las respuestas hasta que actualizas la página para verificar si hay mensajes nuevos, porque no pueden ver tu último mensaje hasta que lo actualizas y así es como funciona HTTP”.
Y ahora viene la mejor parte: para las aplicaciones full-stack que funcionan en tiempo real, existe WebSockets. Esta parte es muy interesante, así que unámonos a Ernie y Bert y descubramos cómo funcionan los WebSockets, sus procedimientos de implementación, el motivo de su importancia y mucho más.
Los WebSockets son similares a una llamada telefónica continua entre un cliente (su navegador web) y un servidor. Se diferencian del protocolo HTTP, que funciona de la manera siguiente: “enviar una carta y esperar una respuesta”. Con WebSockets, ambas partes pueden conversar libremente como quieran. Las características de WebSockets incluyen lo siguiente:
Ejemplo: imaginemos un marcador deportivo que se actualiza constantemente. Con HTTP, el cliente tiene que enviar spam al servidor cada segundo: “¿Ha habido algún cambio en el marcador?”. Con WebSockets, el servidor anuncia “¡GOL!” en el instante en que se produce el error.
Bert se recuesta y se rasca la cabeza. “Pero HTTP funciona bien para la mayoría de las aplicaciones. ¿Por qué cambiar los parámetros?” Ernie se ríe suavemente, toma un marcador y comienza a escribir algo en la pizarra.
“Empecemos por el principio”, dice. “Supongamos que se trata de actualizaciones en tiempo real, como los resultados deportivos en directo. Los clientes tienen que preguntar al servidor cada segundo: ‘¿Hay algo que necesite saber?’. Eso es una encuesta, por lo que cada treinta segundos hay que preguntar: ‘¿Nos reuniremos?’. Es ridículo, ¿no? Además, consume mucho ancho de banda”.
Los WebSockets revierten esto:
“HTTP es bastante bueno para manejar elementos estáticos como un blog o una página de producto. Sin embargo, cuando se trata de interacciones en vivo, se requiere una conexión persistente. WebSockets”.
Ernie se limpia las gafas. "Vamos a crear una aplicación de chat sencilla", exclama Ernie. "Tendremos un servidor Node.js para el backend y una aplicación React para el frontend. ¿Listo?"
“Consigue un servidor Node.js porque lo vas a necesitar para la parte Socket.IO”, dice Ernie mientras escribe muy rápido:
npm init -y npm install express socket.io
Código del servidor (server.js):
const express = require('express'); const http = require('http'); const { Server } = require('socket.io'); const app = express(); const server = http.createServer(app); const io = new Server(server); // Serve static files (like React's build folder) app.use(express.static('public')); // Handle WebSocket connections io.on('connection', (socket) => { console.log('New user connected:', socket.id); // Listen for Incoming messages from the socket socket.on('sendMessage', (message) => { console.log('Received:', message); // Broadcast the message to everyone io.emit('receiveMessage', message); }); // Handle user disconnects events socket.on('disconnect', () => { console.log('User left:', socket.id); }); }); server.listen(3000, () => { console.log('Visit http://localhost:3000 on a browser, Server started successful on port 3000'); });
“El cliente y el servidor pueden comunicarse entre sí gracias al manejo de conexiones WebSocket por parte de Socket.IO. Cuando el cliente llama a la función (sendMessage), el servidor envía el mensaje a todos los que estén conectados a través de (io.emit). Bastante sencillo, ¿no?”
"Ahora me toca a mí. Vamos a trabajar en el cliente React", dice Bert.
npx create-react-app chat-client cd chat-client npm install socket.io-client
Componente de chat (Chat.js):
import { useState, useEffect } from 'react'; import io from 'socket.io-client'; // Connect to the server const socket = io('http://localhost:3000'); function Chat() { const [message, setMessage] = useState(''); const [messages, setMessages] = useState([]); useEffect(() => { // Listen for new messages socket.on('receiveMessage', (newMessage) => { setMessages([...messages, newMessage]); }); }, [messages]); const sendMessage = () => { if (message.trim()) { socket.emit('sendMessage', message); setMessage(''); } }; return ( <div style={{ padding: '20px' }}> <div> {messages.map((msg, index) => ( <p key={index}>{msg}</p> ))} </div> <input value={message} onChange={(e) => setMessage(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && sendMessage()} /> <button onClick={sendMessage}>Send</button> </div> ); } export default Chat;
Cómo funciona:
Pruébalo:
Esto es lo que Bert pretendía construir en base a la aplicación, pero Ernie menciona que “la mayoría de las aplicaciones de la vida real necesitan un poco de manejo de errores y una forma de expandir el marco para el desarrollo”. Veamos algunos problemas probables.
Implementar una estrategia de reconexión en el cliente React.
socket.on('disconnect', () => { console.log('Disconnected. Trying to reconnect...'); socket.connect(); // Auto-reconnect });
WebSocket seguro utilizando tokens JWT autenticados.
// On login, send a token socket.emit('authenticate', { token: 'USER_JWT' }); // Server verifies token socket.on('authenticate', ({ token }) => { if (isValidToken(token)) { socket.authenticated = true; } else { socket.disconnect(); } });
Introduzca el paso de mensajes entre diferentes servidores utilizando Redis.
npm install redis
Código del servidor:
const redis = require('redis'); const subscriber = redis.createClient(); subscriber.subscribe('chat-channel'); subscriber.on('message', (channel, message) => { io.emit('receiveMessage', message); });
Más allá del chat: cómo editar un documento compartido
Esta es la siguiente idea de Bert: un editor similar a Google Docs. Ernie dice: "Puedes usar web sockets para escuchar los cambios realizados por distintas personas y combinarlos".
Por ejemplo :
El usuario A escribe: “Hola”
El usuario B elimina “o” y agrega “, Mundo”
El servidor escucha cada pulsación de tecla y la transmite a todos los clientes, para que sus respectivos clientes muestren un documento ACTUALIZADO.
Fragmento de código:
// Client sends keystrokes textarea.addEventListener('input', (e) => { socket.emit('textChange', e.target.value); }); // Server broadcasts changes socket.on('textChange', (text) => { io.emit('updateText', text); });
Ernie nos advierte que: “¡Nunca deberías usar WebSockets sin HTTPS/WSS!”
const io = new Server(server, { cors: { origin: "https://yourdomain.com" }, });
Valide todas las entradas para desinfectar cualquier dato que pueda conducir a ataques XSS.
Implementar límite de velocidad para usuarios con comportamiento de spam.
P: ¿La velocidad de WebSockets es mucho mejor que la de HTTP?
R: ¡Sí! Para las aplicaciones instantáneas, los WebSockets son los mejores porque eliminan la necesidad de realizar sondeos.
P: ¿Es posible utilizar API REST junto con WebSockets?
R: ¡Sí, definitivamente! Utilice WebSockets con actualizaciones en tiempo real y REST para realizar operaciones CRUD.
P: ¿Cómo puedo actuar si el servidor se bloquea?
R: Los clientes pueden reconectarse automáticamente (ver la lógica de reconexión más arriba).
La aplicación de chat de Bert ahora suena con encanto en tiempo real y Ernie sonríe con satisfacción. Los WebSockets no solo sirven para chats, sino que también sirven para paneles de control en vivo, juegos, IoT y más.
Tu turno: sigue adelante y prueba los ejemplos, destrúyelos y crea algo nuevo. ¡Tus aplicaciones en tiempo real te están esperando!