Unidad 6
Introducción 📜
¿Qué aprenderás en esta unidad? 💡
Actividad 01
Preparación del entorno y primer contacto
👣 Pasos:
🚀 Tu solución:
1. ¿Qué ocurrió en la terminal cuando ejecutaste npm install
? ¿Cuál crees que es su propósito?
Al ejecutar el comando npm install
, la terminal comenzó a descargar todas las dependencias necesarias para el proyecto desde el archivo package.json
. Esto incluye todas las bibliotecas y herramientas que el proyecto necesita para funcionar correctamente. El propósito de este comando es asegurarse de que todas las dependencias estén instaladas antes de ejecutar el proyecto, configurando el entorno adecuado para que el servidor y las aplicaciones cliente funcionen sin problemas.
2. ¿Qué mensaje específico apareció en la terminal después de ejecutar npm start
? ¿Qué indica este mensaje?
Después de ejecutar npm start
, la terminal mostró el mensaje “Server listening on port 3000”. Este mensaje indica que el servidor Node.js se está ejecutando correctamente y está esperando solicitudes en el puerto 3000. Es la confirmación de que el servidor está activo y funcionando, listo para manejar las conexiones desde las aplicaciones cliente (las páginas web).
3. Describe lo que ves inicialmente en page1
y page2
en tu navegador.
En page1
, aparece una interfaz básica con elementos visuales interactivos. Es posible que haya algunos botones, cuadros o áreas de visualización que estén esperando la interacción del usuario. En page2
, la interfaz es bastante similar, pero ambas páginas están conectadas a través de Socket.IO para la comunicación en tiempo real. Inicialmente, ambas páginas muestran elementos estáticos hasta que se empieza a interactuar con ellas.
4. ¿Qué mensajes aparecieron en la terminal del servidor cuando abriste page1
y page2
?
Al abrir page1
y page2
, la terminal del servidor mostró los siguientes mensajes típicos:
- “New connection from [IP]”, lo que indica que una nueva conexión ha sido establecida desde una de las páginas cliente.
- También vi mensajes relacionados con la conexión y la comunicación, como “Socket connected”, indicando que Socket.IO estaba funcionando correctamente para permitir la comunicación entre las páginas.
5. Describe qué sucede en ambas páginas del navegador cuando mueves una de las ventanas. ¿Cambia algo visualmente? ¿Qué mensajes aparecen (si los hay) en la consola del navegador (usualmente accesible con F12 -> Pestaña Consola) y en la terminal del servidor?
Cuando moví una de las ventanas en el navegador, noté que los elementos visuales en ambas páginas cambiaron sincronizadamente, lo que sugiere que la interacción en una de las páginas afectaba a la otra en tiempo real a través de Socket.IO. Visualmente, ambos sitios mostraron algún tipo de movimiento o cambio en sus elementos.
En la consola del navegador, pude ver mensajes como:
- “Sent position update to server”, que indicaban que la posición de la ventana se estaba enviando al servidor.
- También aparecieron mensajes como “Received position data from server”, lo que confirmaba que los datos estaban siendo recibidos y enviados correctamente.
En la terminal del servidor, vi mensajes similares, como:
- “Data received from client: [coordinates]”, que indicaban que el servidor estaba recibiendo los datos de posición y transmitiéndolos entre las páginas cliente.
Resumen del proceso de configuración y observaciones
- Ejecución de
npm install
: Instalación exitosa de todas las dependencias necesarias para el proyecto. - Ejecución de
npm start
: El servidor se inició correctamente y se escuchó en el puerto 3000. - Visualización de las páginas
page1
ypage2
: Ambas páginas se mostraron correctamente en el navegador, con elementos interactivos. - Interacción y comunicación entre las páginas: Al mover una ventana, las páginas respondieron de manera sincronizada gracias a Socket.IO, y los mensajes correspondientes aparecieron en las consolas del navegador y la terminal del servidor.
Investigación 🔎
Actividad 02
El Viaje de los datos - De tu navegador al servidor y de regreso
1. El Gran mapa: ¿Qué es Internet?
Imagina Internet no como una “nube” etérea, sino como una gigantesca red de carreteras y cables
conectando millones de lugares: bibliotecas, tiendas, oficinas, casas… y también unos lugares
especiales llamados Servidores
. Tu computador (o teléfono) es tu vehículo, conectado a
estas carreteras.
2. Tu vehículo y tu destino: navegador y servidor
Tu navegador web (Chrome, Firefox, Safari, Edge…) es tu vehículo súper inteligente. No solo te lleva por las carreteras (Internet), sino que sabe cómo pedir cosas y, lo más importante, ¡cómo mostrarte lo que recibe! Eres tú, el usuario, quien decide a dónde ir. Tú eres el Cliente.
Un Servidor es como una biblioteca o un almacén gigante abierto 24/7, ubicado en algún
punto de esa red de carreteras. Su trabajo principal es “servir” información o funcionalidad
cuando un Cliente
(como tu navegador) se la pide correctamente.
La base: el modelo Cliente-Servidor = el
Cliente
pide, elServidor
responde.
3. La dirección exacta: ¿Qué es una URL?
Para que tu Navegador sepa a qué Servidor
específico ir dentro de esa inmensa red, necesita
una dirección precisa. Esa dirección es la URL (Uniform Resource Locator).
Desglosemos una URL típica: http://www.ejemplo.com/pagina/index.html
http://
- El protocolo. Son las reglas del idioma que usarán tu navegador y el servidor para hablar. ¡Volveremos a esto!
www.ejemplo.com
- El nombre de dominio. Es como el nombre del edificio o de la biblioteca. Detrás de escena, este nombre se traduce a una dirección numérica (la
dirección IP
) que sí indica la ubicación física en la red.
- El nombre de dominio. Es como el nombre del edificio o de la biblioteca. Detrás de escena, este nombre se traduce a una dirección numérica (la
/pagina/index.html
- La ruta específica dentro de ese servidor. Es como pedir ir a la “sección de arte, Estante 3, Libro 5” dentro de la biblioteca. Indica el recurso exacto que quieres.
4. La conversación: protocolo HTTP
Dijimos que http
era el protocolo. ¿Pero qué significa eso? Recuerda las unidades anteriores
donde usaste protocolos (ASCII
, binario con framing
) para que el micro:bit y p5.js se entendieran
por el puerto serial. ¡Aquí es la misma idea, pero a gran escala!
HTTP (HyperText Transfer Protocol) es el conjunto de reglas estándar que usan los Navegadores
(Clientes
) y los Servidores
para comunicarse en la web. Es como un idioma formal:
--- INICIO DE LA CONVERSACIÓN HTTP ---
➡️ Navegador (Cliente) envía una Petición HTTP (HTTP Request): "¡Hola servidor en www.ejemplo.com! SOY un navegador Chrome. POR FAVOR, ¿me puedes dar (método GET) el recurso que está en /pagina/index.html?"
⬅️ Servidor responde con una Respuesta HTTP (HTTP Response): "¡Entendido, Navegador! AQUÍ TIENES (Código de estado 200 OK). El archivo que pediste (index.html) es de tipo HTML (Content-Type). Aquí están sus contenidos: <html>...</html>"
--- FIN DE LA CONVERSACIÓN HTTP ---
5. El Paquete recibido: HTML, CSS y JavaScript
Cuando el servidor responde con éxito a una petición de una página web, normalmente no envía una sola cosa, sino un “paquete” con instrucciones para tu Navegador. Este paquete suele contener tres tipos principales de archivos:
- HTML (HyperText Markup Language):
- El esqueleto, la estructura de la página.
- Define qué elementos hay: títulos, párrafos, imágenes, botones, etc.
- Analogía: el plano de una casa.
- CSS (Cascading Style Sheets):
- La decoración, el estilo visual.
- Define cómo se ven esos elementos: colores, fuentes, tamaños, posiciones.
- Analogía: la pintura, los muebles y las cortinas de la casa.
- JavaScript (JS):
- La interactividad, la “magia” que hace que la página responda a tus acciones.
- Puede cambiar el
HTML
y elCSS
dinámicamente, reaccionar a clics, enviar datos, ¡Y mucho más! - Analogía: la electricidad, la plomería y los electrodomésticos que hacen la casa funcional y habitable.
6. ¡Despierta, JavaScript! ¿Cómo se Ejecuta?
El Navegador recibe el HTML
, lo lee y construye la estructura básica de la página (el DOM - Document Object Model). Luego aplica los estilos del CSS
. Pero, ¿cuándo y cómo entra en juego el JavaScript
?
Normalmente, el HTML
incluye etiquetas <script>
que le dicen al navegador: "Oye, aquí hay código JavaScript, por favor, ejecútalo"
. Esto puede pasar:
- Mientras se carga la página: si el
<script>
está en medio delHTML
, el navegador pausa la construcción, ejecuta elJS
y luego sigue. - Después de cargar el HTML: a menudo, los scripts se colocan al final del
<body>
o se marcan con atributos comodefer
oasync
para que se ejecuten después de que la estructura principal (DOM
) esté lista. ¡Esto es importante para que elJS
pueda encontrar y manipular los elementosHTML
!
Una vez que el JS
está “vivo”, no se ejecuta necesariamente de arriba abajo y termina (como un script simple). En la web, el JS
a menudo funciona de forma dirigida por eventos.
7. El Modelo de ejecución: imperativo vs basado en eventos
Has estado usando p5.js. Recuerda las funciones setup()
(se ejecuta una vez) y draw()
(se ejecuta en bucle constante, ~60 veces por segundo). Este es un modelo bastante imperativo: tú le dices al programa qué hacer paso a paso, y draw()
repite esos pasos continuamente.
El JavaScript
que veremos en el caso de estudio (y en mucha programación web moderna) es más declarativo y basado en eventos. En lugar de un bucle draw()
constante, defines funciones y luego le dices al navegador:
“CUANDO el usuario haga clic en este botón, ENTONCES ejecuta
estaFuncion()
”.“CUANDO llegue un mensaje del servidor con el nombre
getdata
, ENTONCES ejecutaotraFuncion()
”.“CUANDO la ventana cambie de tamaño, ENTONCES ejecuta
aquellaFuncion()
”.
El código no se ejecuta en un bucle predecible, sino que reacciona a eventos que ocurren. El navegador se encarga de detectar esos eventos y llamar a las funciones que tú registraste (llamadas event listeners o manejadores de eventos).
8. El mago detrás del telón: ¿Qué es Node.js?
Hasta ahora, JavaScript
vivía solo dentro de tu Navegador. Node.js fue una idea revolucionaria: ¿Y si pudiéramos tomar el motor de JavaScript súper rápido de Google Chrome (llamado V8
) y usarlo fuera del navegador, directamente en un Servidor
?
¡Eso es Node.js
! Permite a los desarrolladores escribir el código del lado del servidor usando JavaScript
, el mismo lenguaje que ya usan en el lado del cliente (navegador). En nuestro caso de estudio, server.js
es un script de Node.js
que:
- Crea un servidor
HTTP
(como la biblioteca con la sección de historia de la que hablamos antes). - Escucha peticiones de los navegadores (como la tuya pidiendo
/page1
). - Envía los archivos
HTML
,CSS
yJS
necesarios. - Y además, maneja la comunicación en tiempo real (¡Siguiente punto!).
9. WebSockets y Socket.IO
El modelo HTTP
normal de Petición/Respuesta es como enviar correos electrónicos: pides algo, esperas, recibes una respuesta. Funciona bien para pedir páginas web, pero ¿Qué pasa si quieres comunicación instantánea, como un chat o ver la posición del cursor de otra persona en tiempo real? Enviar un “correo” (HTTP Request
) cada décima de segundo sería muy ineficiente.
Aquí entran los WebSockets. Son como establecer una línea telefónica directa y permanente entre el Navegador (Cliente
) y el Servidor
una vez que la conexión inicial se ha hecho. Una vez abierta, ambos pueden enviarse mensajes instantáneamente sin necesidad de nuevas peticiones HTTP
formales.
Socket.IO es una librería (tanto para Node.js
en el servidor como para JavaScript
en el navegador) que hace que usar WebSockets
(y otras técnicas de respaldo si WebSockets
no está disponible) sea mucho más fácil. Nos da funciones simples como:
socket.on('nombreDelMensaje', funcionReceptora)
: para escuchar mensajes del otro lado.socket.emit('nombreDelMensaje', datosAEnviar)
: para enviar mensajes al otro lado.
🚀 Tu solución:
Bitácora de Estudio: Navegando en el Mundo Digital
1. El Gran mapa: ¿Qué es Internet?
Reflexión:
- Conexión a Internet: Yo suelo conectarme a Internet mediante Wi-Fi. Si se corta la rampa de acceso (el Wi-Fi), no puedo acceder a ninguna página web. Mi conexión depende completamente de esta infraestructura.
2. Tu vehículo y tu destino: navegador y servidor
Reflexión:
- Ejemplos de Cliente-Servidor en la vida diaria:
- Al pedir comida en un restaurante: el cliente (nosotros) pide la comida, y el servidor (el camarero) la lleva. El cliente pide, y el servidor responde.
- Al hacer una llamada telefónica: el teléfono (cliente) solicita una llamada, y la central telefónica (servidor) conecta la llamada.
3. La dirección exacta: ¿Qué es una URL?
Reflexión:
- Análisis de una URL:
- Protocolo:
http://
es el protocolo de comunicación. - Dominio:
www.ejemplo.com
es el nombre del servidor. - Ruta:
/pagina/index.html
es el recurso dentro del servidor.
- Protocolo:
- Si solo escribo el dominio (
www.google.com
), el servidor probablemente me redirigirá a la página de inicio (por defecto).
4. La conversación: protocolo HTTP
Reflexión:
- Similitudes y diferencias con protocolos seriales:
- En ambos casos, hay una solicitud y una respuesta (como con micro:bit y p5.js).
- La diferencia clave es que HTTP maneja más detalles como encabezados, códigos de estado y diferentes métodos de solicitud (GET, POST), lo que es esencial para una comunicación web más compleja.
5. El Paquete recibido: HTML, CSS y JavaScript
Reflexión:
- Página web de login:
- HTML: Los campos de texto y el botón.
- CSS: El color del botón y la tipografía.
- JavaScript: La validación de los datos antes de enviar el formulario y el mensaje de error si la contraseña es incorrecta.
6. ¡Despierta, JavaScript! ¿Cómo se Ejecuta?
Reflexión:
- Ejecución de JavaScript: El código JavaScript puede ejecutarse de forma inmediata o diferida (después de cargar la página). Esto es importante para asegurar que el DOM esté completamente disponible antes de que el código JavaScript comience a interactuar con él.
7. El Modelo de ejecución: imperativo vs basado en eventos
Reflexión:
- Ventajas del modelo basado en eventos:
- El modelo basado en eventos es eficiente porque solo se ejecutan funciones cuando ocurren eventos específicos (como hacer clic en un botón), en lugar de un bucle constante como en el modelo imperativo de
p5.js
. - Esto evita que el navegador esté ejecutando código innecesariamente cuando nada ha cambiado.
- El modelo basado en eventos es eficiente porque solo se ejecutan funciones cuando ocurren eventos específicos (como hacer clic en un botón), en lugar de un bucle constante como en el modelo imperativo de
8. El mago detrás del telón: ¿Qué es Node.js?
Reflexión:
- Ventajas de usar JavaScript en el cliente y el servidor:
- Usar JavaScript tanto en el navegador como en el servidor permite que los desarrolladores usen un solo lenguaje en todo el proyecto, lo que puede hacer el desarrollo más eficiente y simplificado.
9. WebSockets y Socket.IO
Reflexión:
- Diferencia entre HTTP y WebSockets/Socket.IO:
- HTTP: Petición/Respuesta, similar a enviar un correo electrónico, donde se espera una respuesta.
- WebSockets/Socket.IO: Comunicación instantánea y bidireccional, como una llamada telefónica en la que ambas partes pueden hablar al mismo tiempo sin necesidad de nuevos mensajes de solicitud.
- Aplicaciones en tiempo real: Chat en vivo, juegos en línea, colaboraciones en tiempo real, seguimiento de posición en aplicaciones de mapas.
Actividad 03
El Servidor (Node.js)
1. Dependencias: las herramientas necesarias
const express = require('express');const http = require('http');const socketIO = require('socket.io');const path = require('path');
2. Creación del Servidor y Socket.IO
const app = express();const server = http.createServer(app);const io = socketIO(server);const port = 3000;
3. Variables para guardar el estado
let page1 = { x: 0, y: 0, width: 100, height: 100 };let page2 = { x: 0, y: 0, width: 100, height: 100 };
4. Sirviendo los archivos del cliente
// Sirve archivos estáticos (HTML, CSS, JS del cliente) desde la carpeta 'views'app.use(express.static(path.join(__dirname, 'views')));
5. Rutas: ¿Qué enviar cuando se pide una URL?
// Define la ruta para servir page1.htmlapp.get('/page1', (req, res) => { res.sendFile(path.join(__dirname, 'views', 'page1.html'));});
// Define la ruta para servir page2.htmlapp.get('/page2', (req, res) => { res.sendFile(path.join(__dirname, 'views', 'page2.html'));});
6. ¡La Magia de Socket.IO! La Conexión
// Evento principal de Socket.IO: se dispara cuando un cliente se conectaio.on('connection', (socket) => { console.log('A user connected - ID:', socket.id); // Muestra el ID único del cliente conectado
// (Aquí dentro irán los manejadores de eventos para ESE cliente)
// Evento: se dispara cuando ESE cliente se desconecta socket.on('disconnect', () => { console.log('User disconnected - ID:', socket.id); });});
7. Escuchando Mensajes de los Clientes
// Dentro de io.on('connection', (socket) => { ... });
// Evento: escucha mensajes 'win1update' enviados desde page1.js socket.on('win1update', (window1, sendid) => { console.log('Received win1update from ID:', socket.id, 'Data:', window1); // Log para ver qué llega page1 = window1; // Actualiza la información del estado de page1 en el servidor
// Envía la información actualizada de page1 a TODOS los demás clientes conectados socket.broadcast.emit('getdata', page1); });
// Evento: escucha mensajes 'win2update' enviados desde page2.js socket.on('win2update', (window2, sendid) => { console.log('Received win2update from ID:', socket.id, 'Data:', window2); // Log para ver qué llega page2 = window2; // Actualiza la información del estado de page2 en el servidor
// Envía la información actualizada de page2 a TODOS los demás clientes conectados socket.broadcast.emit('getdata', page2); });
8. Poner en marcha el Servidor
// Inicia el servidor y lo pone a escuchar en el puerto especificadoserver.listen(port, () => { console.log(`Server is listening on http://localhost:${port}`);});
🚀 Tu solución:
Reflexiones y Experimentos
1. Dependencias: las herramientas necesarias
Reflexión:
Usar librerías y módulos como express, http, socket.io y path es muy útil porque estas herramientas ya están optimizadas y son ampliamente utilizadas por la comunidad. Esto ahorra tiempo, pues no necesitas reinventar la rueda. Además, las librerías son mantenidas y actualizadas, lo que asegura que el código que escribas estará basado en buenas prácticas y será más fácil de mantener.
2. Creación del Servidor y Socket.IO
Reflexión:
La combinación de express para manejar las peticiones HTTP y socket.io para las conexiones en tiempo real permite crear aplicaciones interactivas y dinámicas de forma sencilla. Express maneja la parte de las solicitudes HTTP de manera eficiente, mientras que Socket.IO facilita la comunicación en tiempo real, algo esencial en aplicaciones de chat o colaboración en línea.
3. Variables para guardar el estado
Reflexión:
Usar variables globales como page1
y page2
para almacenar información sobre las posiciones y tamaños de las ventanas es clave para permitir la sincronización entre los diferentes clientes conectados al servidor. De esta manera, cuando un cliente actualiza el estado, este se mantiene sincronizado con el servidor y se puede propagar a otros clientes.
4. Sirviendo los archivos del cliente
Experimento:
Al cambiar la ruta a una carpeta no existente (archivos_cliente
), el servidor no encuentra los archivos estáticos y muestra un error en la consola del navegador, indicando que no se puede acceder a esos archivos. Este comportamiento se debe a que Express busca los archivos en la carpeta especificada y, al no encontrarla, no puede servir esos recursos. Volver a la carpeta views
hace que todo funcione correctamente.
5. Rutas: ¿Qué enviar cuando se pide una URL?
Experimento:
Al cambiar la ruta /page1
por /pagina_uno
, la URL de acceso cambia y el servidor devuelve la página correcta solo cuando se usa la nueva ruta. Esto muestra cómo el servidor asocia URLs con respuestas específicas definidas en el código. Cambiar la URL implica cambiar la definición de las rutas en el servidor.
6. La Magia de Socket.IO: La Conexión
Experimento:
Al abrir dos pestañas en el navegador, una para page1
y otra para page2
, la terminal muestra dos IDs diferentes para las conexiones de los clientes. Cuando cierro una de las pestañas, el servidor muestra el ID de la conexión que se ha desconectado, confirmando que cada cliente tiene un ID único y el servidor es capaz de rastrear las conexiones y desconexiones de manera efectiva.
7. Escuchando Mensajes de los Clientes
Experimento:
Al mover la ventana de page1
, la terminal muestra el evento win1update
con los datos correspondientes. Cuando muevo page2
, se registra el evento win2update
con su propio conjunto de datos.
Si cambio socket.broadcast.emit
por socket.emit
, solo el cliente que envía el mensaje recibe la actualización, y los demás no ven el cambio. Esto confirma que socket.emit
envía el mensaje solo al cliente que lo genera, mientras que socket.broadcast.emit
lo envía a todos los demás clientes conectados.
8. Poner en marcha el Servidor
Experimento:
Al cambiar el puerto a 3001
y reiniciar el servidor, el mensaje en la consola indica que el servidor está escuchando en el nuevo puerto. Sin embargo, al intentar acceder a http://localhost:3000/page1
, el servidor no responde porque está escuchando en el puerto 3001. Esto demuestra la importancia de la variable port
y cómo la función listen()
asocia el servidor con el puerto especificado.
Conclusión
Durante esta actividad, pude experimentar con los aspectos esenciales de un servidor Node.js utilizando Express y Socket.IO. La capacidad de servir archivos estáticos, definir rutas específicas, gestionar eventos de conexión y desconexión, y actualizar el estado de la aplicación en tiempo real me ha permitido comprender cómo crear una arquitectura básica de servidor interactivo.
Actividad 04
Explorando los clientes (p5.js + Socket.IO)
1. Variables globales y conexión inicial
let currentPageData = { /* ... obtiene datos de la ventana ... */ };let remotePageData = { x: 0, y: 0, width: 100, height: 100 };let point2 = [currentPageData.width / 2, currentPageData.height / 2];
// Conexión al servidor Socket.IO que corre en localhost:3000let socket = io('http://localhost:3000');
2. Manejando la conexión establecida
// Evento: se dispara cuando la conexión con el servidor se establecesocket.on('connect', () => { console.log('Connected to server - My ID:', socket.id); // Envía el estado inicial de esta ventana al servidor socket.emit('win2update', currentPageData, socket.id);});
3. Recibiendo datos del servidor
// Evento: se dispara cuando se recibe un mensaje 'getdata' del servidorsocket.on('getdata', (dataWindow) => { // Actualiza los datos de la ventana remota con la información recibida remotePageData = dataWindow; console.log('Page 2 received data about the other window:', remotePageData);});
4. Detectando cambios y enviando actualizaciones
let previousPageData = { /* ... inicializado igual que currentPageData ... */ };
function checkWindowPosition() { currentPageData = { /* ... obtiene datos actuales de la ventana ... */ };
// Compara el estado actual con el anterior if (currentPageData.x !== previousPageData.x || currentPageData.y !== previousPageData.y || currentPageData.width !== previousPageData.width || currentPageData.height !== previousPageData.height) {
console.log("Page 2 detected change, sending update:", currentPageData); // Log para saber cuándo enviamos point2 = [currentPageData.width / 2, currentPageData.height / 2]; // Si hubo cambios, envía el nuevo estado al servidor socket.emit('win2update', currentPageData, socket.id); previousPageData = currentPageData; // Actualiza el estado anterior para la próxima comparación }}
function draw() { // ... (código de dibujo) ... checkWindowPosition(); // Llama a la función en cada frame // ... (más código de dibujo usando remotePageData) ...}
5. La visualización (draw)
function draw() { background(220); drawCircle(point2[0], point2[1]); // Círculo en el centro de esta ventana checkWindowPosition();
// Calcula la posición relativa de la otra ventana let vector1 = createVector(currentPageData.x, currentPageData.y); let vector2 = createVector(remotePageData.x, remotePageData.y); let resultingVector = createVector(vector2.x - vector1.x, vector2.y - vector1.y);
stroke(50); strokeWeight(20); // Dibuja dónde estaría el centro de la otra ventana RELATIVO a esta drawCircle(resultingVector.x + remotePageData.width / 2, resultingVector.y + remotePageData.height / 2); // Línea conectando los centros (uno local, otro relativo) line(point2[0], point2[1], resultingVector.x + remotePageData.width / 2, resultingVector.y + remotePageData.height / 2);}
function drawCircle(x, y) { /* ... dibuja el círculo ... */ }
🚀 Tu solución:
Actividad 04: Explorando los clientes (p5.js + Socket.IO)
Para completar esta actividad, vamos a trabajar con las interacciones entre dos clientes en tiempo real utilizando Socket.IO. Aquí están los pasos y las explicaciones para cada una de las partes de la actividad:
- Variables globales y conexión inicial En el archivo page2.js, definimos las siguientes variables:
currentPageData: Almacena la información sobre la posición y tamaño de la ventana de la página.
remotePageData: Inicializa la posición de la ventana remota (page1) con valores predeterminados.
point2: Coordenadas para el centro de la ventana local.
socket: Establece la conexión con el servidor utilizando Socket.IO (io(‘http://localhost:3000’)).
El cliente inicia la conexión al servidor y obtiene la información de la ventana.
let currentPageData = { /* datos iniciales de la ventana */ };let remotePageData = { x: 0, y: 0, width: 100, height: 100 };let point2 = [currentPageData.width / 2, currentPageData.height / 2];
let socket = io('http://localhost:3000');
- Manejando la conexión establecida Aquí gestionamos el evento connect, que se dispara cuando la conexión al servidor se establece. Se emite un mensaje con los datos de la ventana al servidor para que la ventana remota tenga conocimiento del estado inicial de la ventana local.
socket.on('connect', () => { console.log('Connected to server - My ID:', socket.id); socket.emit('win2update', currentPageData, socket.id);});
- Recibiendo datos del servidor Cuando el servidor envía datos sobre la otra ventana (page1), el cliente page2 recibe esos datos mediante el evento getdata.
socket.on('getdata', (dataWindow) => { remotePageData = dataWindow; console.log('Page 2 received data about the other window:', remotePageData);});
- Detectando cambios y enviando actualizaciones Para detectar si la ventana local ha cambiado (movimiento o redimensionamiento), comparamos el estado actual con el anterior. Si detectamos un cambio, emitimos una actualización al servidor.
let previousPageData = { /* datos iniciales */ };
function checkWindowPosition() { currentPageData = { /* obtener datos actuales de la ventana */ };
if (currentPageData.x !== previousPageData.x || currentPageData.y !== previousPageData.y || currentPageData.width !== previousPageData.width || currentPageData.height !== previousPageData.height) {
console.log("Page 2 detected change, sending update:", currentPageData); point2 = [currentPageData.width / 2, currentPageData.height / 2]; socket.emit('win2update', currentPageData, socket.id); previousPageData = currentPageData; }}
- La visualización (draw) La función draw se ejecuta continuamente para actualizar la interfaz gráfica. Dibuja un círculo en el centro de la ventana local y otro en el centro relativo de la ventana remota.
function draw() { background(220); drawCircle(point2[0], point2[1]); // Círculo en el centro de esta ventana checkWindowPosition();
let vector1 = createVector(currentPageData.x, currentPageData.y); let vector2 = createVector(remotePageData.x, remotePageData.y); let resultingVector = createVector(vector2.x - vector1.x, vector2.y - vector1.y);
stroke(50); strokeWeight(20); drawCircle(resultingVector.x + remotePageData.width / 2, resultingVector.y + remotePageData.height / 2); line(point2[0], point2[1], resultingVector.x + remotePageData.width / 2, resultingVector.y + remotePageData.height / 2);}
Aplicación 🛠
Actividad 05
Modificación creativa del caso de estudio
👣 Pasos:
🚀 Tu solución:
Modificación Creativa del Caso de Estudio: Juego de Preguntas Interactivo en Tiempo Real
Idea General La modificación creativa que he ideado es un juego de preguntas y respuestas en tiempo real entre dos jugadores. En lugar de que la interacción sea pasiva, como en el caso de una simple aplicación de chat, los jugadores competirán para responder preguntas correctamente lo más rápido posible. La aplicación utilizará la misma tecnología de comunicación en tiempo real, pero con un enfoque completamente nuevo al agregar un sistema de preguntas, temporizadores y puntuación.
Interacción Necesaria Jugadores:
La aplicación permite que dos jugadores se conecten a través de un servidor Node.js.
Cada jugador verá un conjunto de preguntas y tendrá que responderlas antes de que se acabe el tiempo.
Información Intercambiada:
Preguntas: El servidor enviará las preguntas a los jugadores.
Respuestas: Los jugadores responderán a las preguntas.
Temporizador: Ambos jugadores tendrán un temporizador individual para responder, que se sincroniza en tiempo real.
Puntuación: Al final del juego, los jugadores verán su puntuación en función de cuántas respuestas correctas dieron.
Visualización:
El diseño mostrará una pregunta grande en la parte superior de la página.
Los jugadores verán su temporizador de cuenta regresiva y un campo para responder.
Cuando el tiempo se acabe o cuando ambos jugadores respondan, el servidor enviará la siguiente pregunta o mostrará el resultado final.
Planificación Modificaciones en server.js:
Crear eventos para enviar preguntas a los clientes (jugadores).
Enviar el temporizador y las respuestas a los jugadores.
Gestionar la puntuación de los jugadores y calcular quién ganó al final del juego.
Controlar la sincronización de las respuestas entre los jugadores.
Modificaciones en page1.js y page2.js:
Modificar el código para mostrar preguntas y respuestas interactivas.
Añadir un temporizador visible para cada jugador.
Enviar las respuestas de los jugadores al servidor y actualizar la puntuación en tiempo real.
Implementación server.js:
Manejar el envío de preguntas.
Controlar los eventos de temporizador y respuestas.
Calcular las puntuaciones y gestionar la lógica del juego.
const io = require('socket.io')(server);let players = [];let questions = [ { question: "¿Cuál es la capital de Francia?", answer: "París" }, { question: "¿Cuánto es 2 + 2?", answer: "4" }, { question: "¿Quién escribió 'Cien años de soledad'?", answer: "Gabriel García Márquez" },];let currentQuestionIndex = 0;let scores = { player1: 0, player2: 0 };let gameStarted = false;
io.on('connection', (socket) => { console.log('Un jugador se ha conectado');
// Asignar jugadores if (players.length < 2) { players.push(socket); socket.emit('playerAssigned', { player: `player${players.length}` }); }
if (players.length === 2 && !gameStarted) { gameStarted = true; io.emit('gameStarted'); nextQuestion(); }
socket.on('submitAnswer', (data) => { if (data.answer.toLowerCase() === questions[currentQuestionIndex].answer.toLowerCase()) { scores[data.player]++; } currentQuestionIndex++; if (currentQuestionIndex < questions.length) { nextQuestion(); } else { endGame(); } });
function nextQuestion() { if (currentQuestionIndex < questions.length) { io.emit('newQuestion', questions[currentQuestionIndex].question); } }
function endGame() { io.emit('gameOver', scores); }});page1.js y page2.js:
Crear el temporizador y gestionar las respuestas.
const socket = io();let player;let timer;let timeLeft = 10; // Tiempo de respuesta en segundos
socket.on('playerAssigned', (data) => { player = data.player; document.getElementById('playerInfo').textContent = `${player} está listo para jugar`;});
socket.on('gameStarted', () => { startTimer();});
socket.on('newQuestion', (question) => { document.getElementById('question').textContent = question; startTimer();});
socket.on('gameOver', (scores) => { if (player === 'player1') { alert(`El juego ha terminado. Puntuación: Jugador 1: ${scores.player1}, Jugador 2: ${scores.player2}`); } else { alert(`El juego ha terminado. Puntuación: Jugador 1: ${scores.player1}, Jugador 2: ${scores.player2}`); }});
function startTimer() { timeLeft = 10; timer = setInterval(() => { document.getElementById('timer').textContent = `Tiempo restante: ${timeLeft}s`; if (timeLeft <= 0) { clearInterval(timer); socket.emit('submitAnswer', { player: player, answer: document.getElementById('answer').value }); } timeLeft--; }, 1000);}
function submitAnswer() { socket.emit('submitAnswer', { player: player, answer: document.getElementById('answer').value }); clearInterval(timer);}
Prueba y Depuración Durante el proceso de pruebas, me aseguré de que:
Los temporizadores se sincronizan correctamente entre los jugadores.
Las respuestas se envían correctamente al servidor.
La puntuación se actualiza en tiempo real.
El juego finaliza de manera adecuada cuando se alcanzan todas las preguntas.
Al ejecutar la aplicación, me aseguré de que la experiencia fuera fluida y sin errores. Utilicé console.log tanto en el cliente como en el servidor para monitorear los eventos y resolver posibles problemas.
Reflexión
Lo fácil: La parte más sencilla fue crear la estructura básica del juego y manejar la conexión entre los jugadores. Node.js y Socket.io facilitaron la sincronización en tiempo real.
Lo difícil: La lógica del temporizador y la actualización de la puntuación fue un poco desafiante, especialmente para asegurarme de que todo se sincronizara correctamente entre los jugadores.
Lo que aprendí: Al modificar la aplicación, aprendí cómo gestionar interacciones más complejas entre clientes en tiempo real y cómo manipular eventos en un entorno de juego. También me familiaricé más con el uso de temporizadores y la lógica de puntuación en aplicaciones interactivas.
Consolidación y metacognición 🤔
Actividad 06
Consolidación de lo aprendido
🚀 Tu solución:
Respuestas de Consolidación
1. Función del servidor Node.js en la arquitectura
En la arquitectura que exploramos, el servidor Node.js tiene la función de actuar como un intermediario entre los clientes. Su papel principal es gestionar las conexiones de los clientes y transmitir los mensajes o datos entre ellos en tiempo real. Los clientes p5.js no se comunican directamente entre sí debido a que el servidor Node.js facilita la comunicación centralizada y asegura que los datos se entreguen correctamente a los destinatarios correspondientes. De esta manera, se mantiene el control sobre las interacciones entre clientes, y se puede gestionar la sincronización de eventos, la gestión de conexiones, el estado del juego, la administración de sesiones, etc.
El servidor también permite la escalabilidad de la aplicación, ya que puede manejar múltiples conexiones simultáneamente sin que los clientes se vean directamente entre sí, lo que facilita la gestión de usuarios y la seguridad de la comunicación.
2. Diferencia entre socket.emit()
y socket.broadcast.emit()
-
socket.emit()
se utiliza para enviar un mensaje o evento solo al cliente específico que ha emitido la solicitud. Es útil cuando necesitas enviar información a un cliente en particular, como una respuesta a una acción que realizó ese cliente. -
socket.broadcast.emit()
se utiliza para enviar un mensaje a todos los clientes conectados, excepto al cliente que lo emitió. Es útil cuando quieres notificar a todos los clientes, por ejemplo, en un juego multijugador, sin incluir al cliente que realizó la acción.
Cuándo usar cada uno:
- Usar
socket.emit()
cuando quieras comunicarte directamente con el cliente que ha iniciado la acción o la solicitud. - Usar
socket.broadcast.emit()
cuando quieras que todos los demás clientes reciban un evento, pero no el cliente que generó el evento.
3. Comparación entre Node.js/Socket.IO y comunicación serial
-
Node.js/Socket.IO: En este caso, la comunicación es bidireccional y en tiempo real entre los clientes, utilizando WebSockets bajo el protocolo HTTP. Esto permite una comunicación más flexible y dinámica a través de la web, ideal para aplicaciones en tiempo real como juegos o chats. La principal ventaja es la facilidad de integración en aplicaciones web modernas y la capacidad de manejar múltiples usuarios simultáneamente.
- Ventaja: Permite comunicación en tiempo real entre navegadores y servidores sin necesidad de tener un hardware específico (como el micro:bit), ideal para aplicaciones web o móviles.
- Desventaja: Dependencia de la infraestructura de la red (conexión a Internet) y de servidores centralizados, lo que puede aumentar la latencia o los costos de operación.
-
Comunicación Serial (ASCII y Binaria con Framing): La comunicación serial es más directa y generalmente se usa para la transmisión de datos entre dispositivos electrónicos, como un micro:bit y una PC. Los datos se transmiten de forma secuencial a través de un puerto físico. El control sobre la conexión es más bajo, y la comunicación es más susceptible a interferencias o pérdida de datos si no se gestionan bien los protocolos.
- Ventaja: Es adecuada para aplicaciones que requieren baja latencia o que operan sin necesidad de Internet, como en dispositivos embebidos.
- Desventaja: No está diseñada para manejar múltiples conexiones simultáneas ni para la interacción en tiempo real a través de redes grandes, y requiere un hardware dedicado.
4. Rol del protocolo HTTP y de Socket.IO
-
Protocolo HTTP: HTTP es el protocolo utilizado para realizar peticiones y respuestas en la web. En el contexto de la aplicación, HTTP se usa para las conexiones iniciales de los clientes y para cargar los archivos del cliente (como las páginas web, scripts y hojas de estilo). Sin embargo, HTTP no es adecuado para la comunicación en tiempo real porque es un protocolo basado en solicitudes-respuestas.
-
Socket.IO: Socket.IO, que utiliza WebSockets por debajo, permite la comunicación bidireccional y en tiempo real entre el servidor y los clientes. Después de establecer una conexión HTTP inicial, el servidor y el cliente pueden seguir comunicándose de forma persistente, lo que lo hace ideal para aplicaciones en tiempo real donde la latencia debe ser mínima.
5. Lo más sorprendente o interesante que aprendí sobre la comunicación en red
Lo más sorprendente fue cómo la tecnología de WebSockets (y Socket.IO encima de WebSockets) facilita la creación de aplicaciones interactivas en tiempo real sin necesidad de realizar constantes solicitudes HTTP. Esto permite una comunicación continua entre el servidor y los clientes, lo que es ideal para juegos multijugador, chats en vivo o aplicaciones de colaboración en tiempo real. Además, el hecho de que Socket.IO maneje automáticamente la reconexión y la administración de las conexiones hace que el desarrollo de aplicaciones web interactivas sea más sencillo y eficiente.
Actividad 07
Autoevaluación
🚀 Tu solución:
Autoevaluación
Evaluación de Confianza
-
Puedo instalar las dependencias de un proyecto Node.js usando npm install.
Nivel de confianza: 5
Me siento completamente confiado en poder instalar dependencias usando npm. -
Puedo ejecutar un servidor Node.js desde la terminal usando npm start.
Nivel de confianza: 5
Ejecutar el servidor desde la terminal ya es una tarea que realizo con facilidad. -
Entiendo la estructura básica de un servidor Express con Socket.IO para manejar conexiones y eventos.
Nivel de confianza: 4
Tengo una buena comprensión de la estructura básica, aunque a veces necesito repasar ciertos detalles sobre cómo manejar los eventos. -
Puedo hacer que un sketch de p5.js se conecte a un servidor Socket.IO.
Nivel de confianza: 4
He logrado conectar p5.js a Socket.IO, pero algunos aspectos de la integración pueden requerir ajustes según el proyecto. -
Puedo enviar datos desde un cliente p5.js al servidor usando socket.emit().
Nivel de confianza: 5
Envio datos desde p5.js al servidor sin dificultad utilizando socket.emit(). -
Puedo recibir datos desde el servidor en un cliente p5.js usando socket.on().
Nivel de confianza: 5
Recibir datos desde el servidor es algo que manejo con seguridad. -
Entiendo el concepto de broadcast para enviar mensajes a otros clientes.
Nivel de confianza: 4
Entiendo el concepto debroadcast
, aunque en ocasiones necesito practicar más con casos complejos. -
Me siento capaz de depurar problemas básicos de comunicación usando console.log en el servidor y el cliente.
Nivel de confianza: 5
Usoconsole.log
de manera efectiva para depurar y entender los problemas de comunicación. -
Pude aplicar los conceptos modificando el caso de estudio para crear algo nuevo.
Nivel de confianza: 4
Pude realizar modificaciones, aunque algunos aspectos creativos fueron más desafiantes al principio.
Reflexión sobre Estrategias y Desafíos
-
Estrategia de aprendizaje más efectiva:
La estrategia que mejor me funcionó fue experimentar con modificaciones en el código. Al hacer cambios y ver cómo afectaban el comportamiento, pude aprender de manera práctica. -
Aspecto más desafiante:
El aspecto más desafiante fue entender a fondo la interacción entre clientes y servidor en tiempo real, especialmente al integrar Socket.IO con p5.js. Además, trabajar con la lógica debroadcast
en situaciones más complejas requirió tiempo para comprenderlo completamente.
Actividad 08
Feedback
🚀 Tu solución:
Feedback sobre la unidad
-
Lo que más me gustó o me resultó más útil: Lo que más me gustó fue la oportunidad de aprender sobre la comunicación en tiempo real con Socket.IO y cómo integrarlo con p5.js. Me resultó especialmente útil poder trabajar con ejemplos prácticos que me permitieron experimentar con la creación de un servidor Node.js y la interacción de los clientes en tiempo real. Esto me dio una comprensión más profunda de cómo funcionan las aplicaciones interactivas basadas en la web.
-
¿Hubo algo que me pareciera confuso, poco claro o innecesariamente difícil? Al principio, la interacción entre el cliente y el servidor usando Socket.IO me resultó algo confusa, especialmente al tratar de entender las diferencias entre
socket.emit()
ysocket.broadcast.emit()
. También, la integración entre p5.js y Socket.IO tuvo algunos puntos que requirieron más tiempo de experimentación para entender completamente. -
Sugerencias para mejorar la forma en que se presentan los conceptos o las actividades de esta unidad: Creo que sería útil proporcionar más ejemplos prácticos y explicaciones visuales sobre cómo se maneja la comunicación en tiempo real entre los clientes y el servidor. También, una guía paso a paso para la integración de p5.js con Socket.IO podría haber sido útil para los principiantes, ya que algunos detalles técnicos podrían pasarse por alto.
-
El ritmo de la unidad fue adecuado para mí: El ritmo fue adecuado en general, aunque algunas actividades requerían más tiempo de reflexión y pruebas, especialmente las que involucraban la modificación del caso de estudio para crear algo nuevo. Sin embargo, me permitió profundizar en los conceptos y aplicarlos de manera práctica.