diff --git a/README.md b/README.md new file mode 100644 index 0000000..3210ad3 --- /dev/null +++ b/README.md @@ -0,0 +1,250 @@ +# 🖥️ Proyecto PSP — Programación de Servicios y Procesos + +Aplicación de escritorio desarrollada en Python con Tkinter que agrupa en una sola ventana demostraciones prácticas de los tres grandes temas de la asignatura: **procesos**, **hilos** y **comunicaciones en red**. + +--- + +## 📋 Índice + +- [Descripción general](#descripción-general) +- [Estructura de ficheros](#estructura-de-ficheros) +- [Requisitos e instalación](#requisitos-e-instalación) +- [Cómo ejecutar](#cómo-ejecutar) +- [Pestaña T1 — Procesos](#pestaña-t1--procesos) +- [Pestaña T2 — Hilos](#pestaña-t2--hilos) +- [Pestaña T3 — Comunicaciones](#pestaña-t3--comunicaciones) + - [Chat IPC](#chat-ipc) + - [Gestor de Correo](#gestor-de-correo) +- [Arquitectura técnica](#arquitectura-técnica) +- [Decisiones de diseño destacadas](#decisiones-de-diseño-destacadas) + +--- + +## Descripción general + +La aplicación se organiza en tres pestañas principales (T1, T2, T3) accesibles desde un `ttk.Notebook`. Cada pestaña contiene a su vez un notebook interno con varias sub-pestañas. Una barra de estado en la parte inferior muestra información dinámica de cada módulo activo. + +--- + +## Estructura de ficheros + +``` +ProyectoPython_AdrianHustea/ +│ +├── Proyecto.py # Fichero principal — toda la lógica de la aplicación +├── chat_client.py # Script independiente para las ventanas de chat (subproceso) +├── alarm.mp3 # Sonido de la alarma (T2) +├── music.mp3 # Música de fondo (T2) +├── backup_script.sh # Script de backup del editor de texto (T1) +└── README.md +``` + +> `chat_client.py` **debe estar en la misma carpeta que `Proyecto.py`**. Es lanzado como subproceso independiente al abrir el chat. + +--- + +## Requisitos e instalación + +**Python 3.8 o superior** + +Instalar dependencias: + +```bash +pip install psutil matplotlib pygame +``` + +| Librería | Uso | +|---|---| +| `tkinter` | Interfaz gráfica (incluida en Python estándar) | +| `psutil` | Monitorización de CPU, memoria, disco y red (T1) | +| `matplotlib` | Gráficas en tiempo real de recursos (T1) | +| `pygame` | Reproducción de música y alarma (T2) | +| `imaplib` | Lectura de correo via IMAP (T3 — incluida en Python estándar) | +| `smtplib` | Envío de correo via SMTP (T3 — incluida en Python estándar) | +| `subprocess` | Lanzamiento de subprocesos para el chat (T3 — incluida en Python estándar) | + +--- + +## Cómo ejecutar + +```bash +python3 Proyecto.py +``` + +Para probar el chat de forma independiente: + +```bash +touch /tmp/cola_a.txt /tmp/cola_b.txt + +python3 chat_client.py "Cliente A" "Cliente B" /tmp/cola_a.txt /tmp/cola_b.txt 100 150 +python3 chat_client.py "Cliente B" "Cliente A" /tmp/cola_b.txt /tmp/cola_a.txt 560 150 +``` + +--- + +## Pestaña T1 — Procesos + +Sub-pestañas: + +**Estado del sistema** +Monitorización en tiempo real del sistema usando hilos de fondo (`threading.Thread`) que actualizan gráficas de `matplotlib` embebidas en Tkinter. Métricas disponibles: +- Uso de CPU (%) +- Memoria RAM usada/libre +- Espacio en disco +- Tráfico de red (bytes enviados/recibidos) + +**Editor de texto** +Editor con las operaciones básicas (abrir, guardar, nuevo) y un botón de backup que invoca `backup_script.sh` como subproceso mediante `subprocess.Popen`. + +**Hilo** +Demostración de un hilo de red que realiza peticiones HTTP y muestra el resultado en la interfaz sin bloquear la GUI. + +--- + +## Pestaña T2 — Hilos + +Sub-pestañas: + +**Reloj / Alarma** +Reloj digital que se actualiza cada segundo mediante un hilo dedicado (`threading.Thread` con `stop_event`). Permite programar una alarma con cuenta atrás, reproduciendo `alarm.mp3` a través de `pygame.mixer` cuando se alcanza el tiempo establecido. + +**Coches** +Simulación de una carrera entre varios coches, donde cada coche es un hilo independiente. Se usa un `threading.Lock` para proteger el acceso a la variable `winner` (sección crítica) y un `threading.Event` para detener la carrera limpiamente. + +**Scraping** +Hilo de fondo que realiza scraping de una URL usando `urllib.request` y expresiones regulares, mostrando los resultados en la interfaz sin bloquear la GUI. + +--- + +## Pestaña T3 — Comunicaciones + +Contiene dos sub-pestañas: **Chat** y **Correo**. + +--- + +### Chat IPC + +Comunicación entre procesos (IPC) mediante ficheros de cola compartidos. + +**¿Por qué procesos y no hilos?** +Tkinter no es thread-safe. No es posible tener dos ventanas `tk.Tk()` en hilos distintos del mismo proceso sin que colisionen sobre el display X11. La solución es lanzar **dos procesos completamente independientes**, uno por cada ventana, usando `subprocess.Popen`. + +**Arquitectura:** + +``` +Proyecto.py + │ + ├── subprocess.Popen ──→ chat_client.py "Cliente A" (proceso 1) + └── subprocess.Popen ──→ chat_client.py "Cliente B" (proceso 2) + +Ficheros de cola (en /tmp/chat_ipc_xxxx/): + cola_a.txt ← Cliente A escribe / Cliente B lee + cola_b.txt ← Cliente B escribe / Cliente A lee +``` + +**Protocolo de mensajes:** +Cada mensaje se almacena como una línea de texto con el formato: +``` +HH:MM|texto del mensaje +``` + +**Polling:** +Cada cliente comprueba su fichero de entrada cada 300ms usando `root.after(300, poll_incoming)`. Se usa `root.after()` en lugar de un hilo adicional porque programa la llamada dentro del propio hilo del mainloop de Tkinter, haciendo seguro actualizar los widgets directamente. + +**Paso de configuración:** +Toda la configuración de cada cliente (nombre, rutas de ficheros, posición de ventana) se pasa por argumentos de línea de comandos (`sys.argv`) al lanzar el subproceso. + +--- + +### Gestor de Correo + +Cliente de correo electrónico completo integrado en la aplicación. Configurado para el servidor de clase en `10.10.0.101`. + +**Protocolos utilizados:** + +| Protocolo | Puerto | Uso | +|---|---|---| +| IMAP | 143 | Consulta y lectura de correos (correos permanecen en el servidor) | +| SMTP | 25 | Envío de correos (sin autenticación — relay interno) | +| POP3 | 110 | Definido en configuración (disponible para uso futuro) | + +**Flujo de navegación:** + +``` +Login + └─→ Bandeja de entrada (IMAP) + └─→ Leer correo + └─→ Responder ──→ Formulario de redacción (SMTP) + └─→ Nuevo correo ──→ Formulario de redacción (SMTP) +``` + +Cada pantalla destruye los widgets de la anterior y construye los suyos propios dentro del mismo frame — navegación sin ventanas adicionales. + +**Carga en hilo separado:** +La descarga de correos de la bandeja se realiza en un `threading.Thread(daemon=True)` para evitar que la interfaz se congele durante la conexión al servidor. + +**Cierre blindado de conexiones IMAP:** +Todas las conexiones IMAP usan un bloque `finally` con doble red de seguridad para garantizar que la conexión se cierra siempre, previniendo la acumulación de conexiones zombi que podrían provocar el bloqueo temporal de la IP por parte del servidor: + +```python +finally: + if conn is not None: + try: + conn.logout() # Cierre limpio + except Exception: + try: + conn.shutdown() # Cierre forzado del socket + except Exception: + pass # El GC cerrará el socket +``` + +**Decodificación de cabeceras:** +La función `decode_str()` decodifica cabeceras de email en base64 o quoted-printable (asunto, remitente) para mostrarlas correctamente con tildes y caracteres especiales. + +--- + +## Arquitectura técnica + +``` +┌─────────────────────────────────────────────────────┐ +│ Proyecto.py │ +│ │ +│ ┌─────────┐ ┌─────────┐ ┌──────────────────────┐ │ +│ │ T1 │ │ T2 │ │ T3 │ │ +│ │Procesos │ │ Hilos │ │ ┌──────┬──────────┐ │ │ +│ │ │ │ │ │ │ Chat │ Correo │ │ │ +│ │Estado │ │Reloj │ │ └──────┴──────────┘ │ │ +│ │Editor │ │Alarma │ │ │ │ │ +│ │Hilo red │ │Coches │ │ subprocess.Popen │ │ +│ └─────────┘ │Scraping │ └──────────────────────┘ │ +│ └─────────┘ │ │ +└─────────────────────────────────────│───────────────┘ + │ + ┌──────────────────┴──────────────────┐ + │ chat_client.py │ + │ (proceso independiente × 2) │ + │ Comunicación: ficheros de cola │ + └──────────────────────────────────────┘ +``` + +**Gestión de hilos:** +Cada módulo tiene su propio `stop_event` (`threading.Event`) para detener los hilos de forma limpia al cambiar de pestaña. La función `navigate_to_tab()` se encarga de llamar a todas las funciones de parada antes de inicializar la nueva pestaña. + +--- + +## Decisiones de diseño destacadas + +**Procesos en lugar de hilos para el chat** +Tkinter no es thread-safe para múltiples ventanas. Usar `subprocess.Popen` con `chat_client.py` como script independiente evita completamente el problema de compartir el estado de X11. + +**IPC por ficheros en lugar de sockets o pipes** +Los ficheros de texto plano como canal de comunicación son simples, depurables (se puede leer el fichero directamente) y no requieren gestión de conexiones ni sincronización con semáforos. + +**`root.after()` para el polling** +Usar el scheduler interno de Tkinter en lugar de un hilo de polling mantiene todas las actualizaciones de widgets en el hilo principal del mainloop, evitando condiciones de carrera. + +**`finally` blindado en IMAP** +Lección aprendida durante el desarrollo: sin cerrar explícitamente las conexiones IMAP, las conexiones zombi se acumulan hasta que el servidor bloquea la IP. El bloque `finally` con doble red de seguridad (`logout` → `shutdown` → `pass`) garantiza la liberación del recurso en cualquier escenario. + +**Navegación por destrucción de widgets** +En lugar de mantener múltiples frames ocultos/visibles, cada función de pantalla destruye el contenido anterior del frame padre con `winfo_children()` + `destroy()`. Esto simplifica la gestión de estado y evita widgets huérfanos consumiendo memoria.