Juegos en Red - Grado en Diseño y Desarrollo de Videojuegos
2026-09-09
Socket: extremo de comunicación bidireccional
Ejemplo:
212.128.240.50:100001XX.XXX.XXX.XX:YYYYFlujo típico:
Importante
No es solicitud-respuesta: ambos pueden iniciar comunicación
Protocolo de comunicación full-duplex
Nota
Full-duplex: ambas partes pueden enviar mensajes independientemente
Proceso:
Tip
El servidor puede enviar sin que el cliente pregunte constantemente
RFC 6455 sobre socket TCP
Ventajas:
Nota
Funciona sobre HTTP pero puede usar protocolo propio
Dos esquemas disponibles:
http://https://La negociación comienza sobre HTTP
Petición del cliente:
Connection: Upgrade → queremos cambiar de protocoloUpgrade: websocket → especifica WebSocketSec-WebSocket-Key → valor aleatorio para verificar el handshakeSec-WebSocket-Version: 13 → versión del protocolo (RFC 6455)Origin → origen de la petición (seguridad)Si acepta la conexión:
Sec-WebSocket-Accept → derivado de Sec-WebSocket-Key (demuestra que el servidor entiende WebSocket)Importante
Después del handshake, HTTP se transforma en WebSocket
La negociación es automática
Tip
No necesitas implementar manualmente el handshake HTTP
Por qué usar WebSockets:
Cuándo usar cada uno:
| REST | WebSockets |
|---|---|
| Registro/autenticación | Movimiento de jugadores |
| Puntuaciones | Estado del juego |
| Rankings | Eventos en tiempo real |
| Perfiles | Chat del juego |
| Configuración | Notificaciones instantáneas |
Nota
Ambas tecnologías se complementan en juegos modernos
Instanciar el objeto WebSocket
Componentes:
ws:// o wss:// → protocoloIP → dirección o dominio del servidorPUERTO → puerto donde escucha el servidorRUTA → endpoint específicoEjemplo:
Tip
La conexión se establece automáticamente
send() - Enviar datos al servidor
close() - Cerrar conexión
Nota
close() inicia cierre ordenado de la conexión
Se ejecuta cuando la conexión se abre
Tip
Momento perfecto para enviar mensaje inicial
Se ejecuta al recibir mensaje del servidor
connection.onmessage = function(msg) {
console.log("Mensaje recibido: " + msg.data);
// Si es JSON, parsearlo
const data = JSON.parse(msg.data);
// Procesar según tipo
if (data.type === 'player_position') {
actualizarPosicionJugador(data.playerId, data.x, data.y);
} else if (data.type === 'game_over') {
mostrarPantallaFinJuego(data.winner);
}
};Importante
El dato real está en msg.data
Se ejecuta cuando hay un error
Situaciones que disparan error:
Se ejecuta cuando la conexión se cierra
connection.onclose = function(event) {
console.log('Desconectado del servidor');
console.log('Código de cierre:', event.code);
// Notificar al usuario
alert('Se ha perdido la conexión');
// Intentar reconectar después de 3 segundos
setTimeout(function() {
const nuevaConexion = new WebSocket('ws://127.0.0.1:8080/echo');
// configurarListeners reasigna los manejadores onopen, onmessage,
// onerror y onclose (vistos en este tema) a la nueva conexión
configurarListeners(nuevaConexion);
}, 3000);
};Razones del cierre:
connection.close()// Crear conexión
const ws = new WebSocket('ws://127.0.0.1:8080/game');
// Configurar listeners
ws.onopen = function() {
console.log('Conectado');
ws.send(JSON.stringify({ type: 'join', name: 'Alice' }));
};
ws.onmessage = function(msg) {
const data = JSON.parse(msg.data);
console.log('Recibido:', data);
};
ws.onerror = function(error) {
console.error('Error:', error);
};
ws.onclose = function() {
console.log('Desconectado');
};
// Enviar movimiento
function moverJugador(x, y) {
ws.send(JSON.stringify({ type: 'move', x, y }));
}Implementación robusta y eficiente
Instalación:
Configurar package.json:
import express from 'express';
import { WebSocketServer } from 'ws';
import { createServer } from 'http';
const app = express();
const server = createServer(app);
// Servir archivos estáticos
app.use(express.static('public'));
// Crear servidor WebSocket
const wss = new WebSocketServer({ server });
// Iniciar servidor
const PORT = 8080;
server.listen(PORT, () => {
console.log(`Servidor en http://localhost:${PORT}`);
});Componentes:
createServer)
public para cliente HTML/CSS/JSSe dispara cuando un cliente se conecta
Nota
Cada cliente tiene su propio objeto ws
wss.on('connection', (ws) => {
console.log('Nuevo cliente conectado');
ws.on('message', (message) => {
console.log('Mensaje recibido:', message.toString());
// Si es JSON, parsearlo
const data = JSON.parse(message.toString());
console.log('Nombre:', data.nombre);
console.log('Mensaje:', data.mensaje);
});
});Importante
Los mensajes llegan como Buffer, usar .toString()
A un cliente específico:
Enviar JSON:
Limpiar recursos al desconectar:
wss.on('connection', (ws) => {
console.log('Cliente conectado');
ws.on('close', () => {
console.log('Cliente desconectado');
// Limpiar datos asociados a este cliente
});
ws.on('error', (error) => {
console.error('Error en la conexión:', error);
});
ws.on('message', (message) => {
// Manejar mensajes
});
});Devuelve cualquier mensaje recibido
import express from 'express';
import { WebSocketServer } from 'ws';
import { createServer } from 'http';
const app = express();
const server = createServer(app);
app.use(express.static('public'));
const wss = new WebSocketServer({ server, path: '/echo' });
wss.on('connection', (ws) => {
console.log('Nuevo cliente');
ws.on('message', (message) => {
const data = message.toString();
console.log('Recibido:', data);
// Echo: devolver al cliente
ws.send(`Echo: ${data}`);
});
ws.on('close', () => console.log('Cliente desconectado'));
ws.on('error', (error) => console.error('Error:', error));
});
server.listen(8080, () => console.log('Servidor en puerto 8080'));Enviar mensaje a todos los clientes conectados
Tip
Verificar readyState === WebSocket.OPEN antes de enviar (importando WebSocket de ‘ws’)
import express from 'express';
import { WebSocketServer, WebSocket } from 'ws';
import { createServer } from 'http';
const app = express();
const server = createServer(app);
app.use(express.static('public'));
const wss = new WebSocketServer({ server });
wss.on('connection', (ws) => {
console.log('Cliente conectado al chat');
ws.on('message', (message) => {
try {
const datos = JSON.parse(message.toString());
console.log(`${datos.nombre}: ${datos.mensaje}`);
const respuesta = {
nombre: datos.nombre,
mensaje: datos.mensaje,
timestamp: new Date().toISOString()
};
// Broadcast a todos
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(respuesta));
}
});
} catch (error) {
console.error('Error al parsear:', error);
ws.send(JSON.stringify({ error: 'Formato inválido' }));
}
});
ws.on('close', () => console.log('Cliente desconectado'));
ws.on('error', (error) => console.error('Error:', error));
});
server.listen(8080, () => console.log('Chat en puerto 8080'));Asociar datos a cada conexión
const clientes = new Map();
let nextClientId = 1;
wss.on('connection', (ws) => {
// Asignar ID único
const clientId = nextClientId++;
clientes.set(ws, { id: clientId, nombre: null });
console.log(`Cliente ${clientId} conectado`);
ws.on('message', (message) => {
const datos = JSON.parse(message.toString());
// Guardar nombre del cliente
const clienteInfo = clientes.get(ws);
clienteInfo.nombre = datos.nombre;
// Procesar mensaje...
});
ws.on('close', () => {
const clienteInfo = clientes.get(ws);
console.log(`Cliente ${clienteInfo.id} desconectado`);
clientes.delete(ws);
});
});Combinar lo mejor de ambos mundos
import express from 'express';
import { WebSocketServer } from 'ws';
import { createServer } from 'http';
const app = express();
const server = createServer(app);
app.use(express.json());
app.use(express.static('public'));
// Rutas REST
app.get('/api/status', (req, res) => {
res.json({
clientesConectados: wss.clients.size,
estado: 'activo'
});
});
// Servidor WebSocket
const wss = new WebSocketServer({ server });
wss.on('connection', (ws) => {
// Manejar conexiones WebSocket
});
server.listen(8080);REST para:
WebSocket para:
Importante
Ambos protocolos comparten servidor y puerto
Lo mejor de ambos mundos:
Fundamentos:
ws:// y wss://Ventajas:
Objeto WebSocket:
Métodos:
send(data) - enviar al servidorclose() - cerrar conexiónEvent Listeners:
onopen - conexión establecidaonmessage - mensaje recibidoonerror - error en conexiónonclose - conexión cerradaLibrería ws:
Eventos del servidor:
connection - nuevo cliente conectadomessage - mensaje del clienteclose - cliente desconectadoerror - error en conexiónBroadcast:
Cuándo usar cada tecnología:
| Criterio | REST | WebSockets |
|---|---|---|
| Frecuencia | Ocasional | Continua |
| Latencia | Aceptable | Crítica |
| Dirección | Cliente inicia | Ambos inician |
| Casos de uso | CRUD, auth | Tiempo real, chat |
Tip
En juegos modernos: ambas tecnologías se complementan
