import socket import threading import tkinter as tk from tkinter import ttk, filedialog import os import time # ============================================================ # UTILIDADES DE RED # ============================================================ def obtener_ip_local(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.connect(("8.8.8.8", 80)) ip = s.getsockname()[0] s.close() return ip except: return "0.0.0.0" def obtener_puerto_disponible(): for puerto in range(5000, 5101): try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("0.0.0.0", puerto)) s.close() return puerto except: continue raise Exception("No hay puertos disponibles entre 5000–5100") MI_IP = obtener_ip_local() MI_PUERTO = obtener_puerto_disponible() ANUNCIO_PUERTO = 6000 # UDP broadcast para descubrir dispositivos BUFFER = 4096 # ============================================================ # SERVIDOR TCP RECEPCIÓN ARCHIVOS # ============================================================ def servidor_tcp(): if not os.path.exists("recibidos"): os.makedirs("recibidos") srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.bind(("0.0.0.0", MI_PUERTO)) srv.listen(5) while True: try: conn, addr = srv.accept() info = conn.recv(1024).decode() nombre, tam = info.split("|") tam = int(tam) ruta = f"recibidos/{nombre}" with open(ruta, "wb") as f: recibido = 0 while recibido < tam: datos = conn.recv(BUFFER) if not datos: break f.write(datos) recibido += len(datos) conn.send(b"OK") conn.close() except Exception as e: print("Error en servidor:", e) # ============================================================ # SERVIDOR DE ANUNCIOS UDP # ============================================================ def servidor_anuncios(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) while True: time.sleep(1) anuncio = f"DEVICE|{MI_IP}|{MI_PUERTO}" s.sendto(anuncio.encode(), ('', ANUNCIO_PUERTO)) # ============================================================ # ESCANEAR RED (recibir anuncios) # ============================================================ def descubrir_dispositivos(timeout=2): dispositivos = {} s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(("0.0.0.0", ANUNCIO_PUERTO)) s.settimeout(timeout) start = time.time() while time.time() - start < timeout: try: data, addr = s.recvfrom(1024) msg = data.decode() if msg.startswith("DEVICE"): _, ip, puerto = msg.split("|") # ignoramos nuestro propio equipo if ip != MI_IP: dispositivos[ip] = int(puerto) except: continue return dispositivos # ============================================================ # CLIENTE TCP # ============================================================ def enviar_archivo(ruta, ip, puerto): tam = os.path.getsize(ruta) nombre = os.path.basename(ruta) cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) cli.connect((ip, puerto)) cli.send(f"{nombre}|{tam}".encode()) with open(ruta, "rb") as f: while True: datos = f.read(BUFFER) if not datos: break cli.send(datos) resp = cli.recv(1024) cli.close() return resp == b"OK" # ============================================================ # INTERFAZ TKINTER # ============================================================ class App(tk.Tk): def __init__(self): super().__init__() self.title("Enviador de archivos") self.geometry("420x360") self.resizable(False, False) self.frame_actual = None self.mostrar_home() # -------------------------------------------------------- def reemplazar_pantalla(self, frame): if self.frame_actual: self.frame_actual.destroy() self.frame_actual = frame self.frame_actual.pack(fill="both", expand=True) # -------------------------------------------------------- # PANTALLA HOME # -------------------------------------------------------- def mostrar_home(self): frame = tk.Frame(self) tk.Label(frame, text="Tu IP:", font=("Arial", 16)).pack(pady=10) tk.Label(frame, text=MI_IP, font=("Arial", 20, "bold")).pack(pady=10) ttk.Button(frame, text="Buscar equipos", command=self.mostrar_dispositivos)\ .pack(pady=20) self.reemplazar_pantalla(frame) # -------------------------------------------------------- # LISTA DE DISPOSITIVOS # -------------------------------------------------------- def mostrar_dispositivos(self): frame = tk.Frame(self) tk.Label(frame, text="Buscando equipos...", font=("Arial", 16)).pack(pady=10) self.reemplazar_pantalla(frame) self.update() dispositivos = descubrir_dispositivos() frame = tk.Frame(self) tk.Label(frame, text="Equipos encontrados:", font=("Arial", 16)).pack(pady=10) if not dispositivos: tk.Label(frame, text="No se encontró ningún equipo.", font=("Arial", 14), fg="red").pack(pady=20) ttk.Button(frame, text="Volver", command=self.mostrar_home).pack() self.reemplazar_pantalla(frame) return for ip, puerto in dispositivos.items(): ttk.Button( frame, text=f"{ip}:{puerto}", command=lambda i=ip, p=puerto: self.seleccionar_archivo(i, p) ).pack(pady=5) ttk.Button(frame, text="Volver", command=self.mostrar_home).pack(pady=20) self.reemplazar_pantalla(frame) # -------------------------------------------------------- # SELECCIONAR ARCHIVO # -------------------------------------------------------- def seleccionar_archivo(self, ip, puerto): ruta = filedialog.askopenfilename(title="Seleccionar archivo") if ruta: ok = enviar_archivo(ruta, ip, puerto) if ok: self.mostrar_confirmacion() else: self.mostrar_error() # -------------------------------------------------------- # PANTALLAS DE CONFIRMACIÓN # -------------------------------------------------------- def mostrar_confirmacion(self): frame = tk.Frame(self) tk.Label(frame, text="Archivo enviado con éxito 🎉", font=("Arial", 16), fg="green").pack(pady=40) ttk.Button(frame, text="Volver al inicio", command=self.mostrar_home)\ .pack(pady=10) self.reemplazar_pantalla(frame) def mostrar_error(self): frame = tk.Frame(self) tk.Label(frame, text="Error al enviar archivo", font=("Arial", 16), fg="red").pack(pady=40) ttk.Button(frame, text="Volver", command=self.mostrar_home).pack(pady=10) self.reemplazar_pantalla(frame) # ============================================================ # MAIN # ============================================================ if __name__ == "__main__": # Iniciar servidores en background threading.Thread(target=servidor_tcp, daemon=True).start() threading.Thread(target=servidor_anuncios, daemon=True).start() app = App() app.mainloop()