From d87199c089ad135eeefdb817f3ea6c5301106c9a Mon Sep 17 00:00:00 2001 From: DevManuel19 Date: Fri, 16 Feb 2024 17:32:42 +0100 Subject: [PATCH] Proyecto control de estado de usuarios conectados --- README.txt | 96 ++++++++++++++++++++++++++++ cliente.py | 72 +++++++++++++++++++++ servidorUsuariosActivos.py | 125 +++++++++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 README.txt create mode 100644 cliente.py create mode 100644 servidorUsuariosActivos.py diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..fdb31da --- /dev/null +++ b/README.txt @@ -0,0 +1,96 @@ +SERVIDOR DE ESTADO DE CLIENTES + +REQUISITOS + +· Servidor multiusuario +· Controla en todo momento cuantos clientes tiene conectados (hilos) +· Un cliente cuando se conecta envía su propio nombre. +· Cada 5 minutos envía un mensaje a todos los clientes indicando el número de ordenadores que tiene conectado. +· Si un cliente se desconecta, envía un mensaje a todos avisando que se ha desconectado + +------------ +| SERVIDOR | +------------ + +-- MÉTODO INICIAR SERVIDOR + +Este método se encarga de iniciar el servidor. Toma como argumentos el host (dirección IP) y el puerto en el que se escucharán las conexiones entrantes de los clientes, con ello crea un socket y lo enlaza al host y puerto especificado. + +Pone el socket en modo de escucha para aceptar conexiones entrantes e inicia un bucle infinito para escuchar conexiones. +Por cada conexión entrante, crea un hilo separado para manejar al cliente. + + +-- MANEJAR_CLIENTES + +Este metodo maneja la forma de interactuar del cliente con el servidor de forma individual, recibe como argumentos el socket del cliente y el diccionario de los clientes conectados. + +Espera a recibir el nombre del cliente y luego lo agrega al diccionario. + + +En un bucle infinito, espera 5 segundos y luego: +Actualiza la lista de usuarios y el contador de usuarios en la interfaz gráfica del servidor. +Envía al cliente un mensaje con el número de clientes conectados. +Maneja cualquier excepción que pueda ocurrir durante la comunicación con el cliente, eliminando al cliente del diccionario y cerrando su socket. +Informa a los demás clientes sobre la desconexión del cliente actual. + + +ACTUALIZAR LISTA USUARIOS + +Este método actualiza la lista de usuarios en la interfaz gráfica del servidor. +Este método se encarga de actualizar la lista de usuarios de la interfaz gráfica asocciada al servidor, como argumentos recibe el diccionario de clientes y obtiene los nombres. + +Cada vez limia la interfaz y los agrega a la vista. + +ACTUALIZAR CONTADOR USUARIOS + +Este método actualiza el contador de usuarios en la interfaz gráfica del servidor, recibe como argumento el contador de usuarios. +Actualiza el texto del contador con el número de usuarios conectados. + + +GENERAR INTERFAZ + +Este métods se encarga de generar la interfaz grafica estableciendo el diseño correspondiente, +con un contador y una lista de usuarios conectados. +Espera en bucle la interaccion de los clientes. + +MAIN + +Simplemente es el bloque de inicializacion de la aplicacion + +------------ +| CLIENTE | +------------ + +PRINCIPAL: + +Esta funcion se encarga de generar la interfaz gráfica del cliente, utilizando Tkinter, +ademas de establecer una entrada de texto para el nombre del cliente junto con dos botones para +enviar el nombre del cliente al servidor y desconectarse del mismo. +Esta función es el punto de entrada principal para la interfaz gráfica del cliente. + +Establece los eventos de clic de los botones para llamar a las funciones correspondientes enviar_nombre_callback y desconectar. + +ENVIAR NOMBRE + +Esta función se encarga de enviar el nombre ingresado por el usuario al servidor. +Recibe como argumentos el nombre ingresado por el usuario, el campo de entrada de nombre y el botón de enviar. + +Si la conexión esta en modo activa, envia el nombre al servidor utilizando el socket del cliente. +Espera recibir la respuesta del servidor para imprimirlo por consola. + +Deshabilita el campo de entrada y el botón de enviar después de enviar el nombre para evitar que el cliente haga envios repetidos. + + +DESCONECTAR + +Esta función se encarga de desconectar al cliente del servidor. +Si la conexión está activa (true), envía al servidor un mensaje para desconectarse y cierra el socket del cliente. +Después de desconectar, cambia el estado de conectado (false). + + +MAIN + +Simplemente es el bloque de inicializacion de la aplicacion + + + diff --git a/cliente.py b/cliente.py new file mode 100644 index 0000000..14f336c --- /dev/null +++ b/cliente.py @@ -0,0 +1,72 @@ +import socket +import tkinter as tk + +# CREAR SOCKET TCP/IP +socket_cliente = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +socket_cliente.connect(('localhost', 8088)) + +#ESTADO CONEXION +conectado = True + +def principal(): + + # VENTANA + raiz = tk.Tk() + raiz.title("Cliente") + + # LABEL INGRESAR NOMBRE + entrada_nombre = tk.Entry(raiz, font=("Arial", 14)) + entrada_nombre.pack(pady=10) + + # ENVIAR NOMBRE AL SERVIDOR + def enviar_nombre_callback(): + nombre = entrada_nombre.get() + enviar_nombre(nombre, entrada_nombre, boton_enviar) + + # Botón para enviar el nombre + boton_enviar = tk.Button(raiz, text="Enviar Nombre", command=enviar_nombre_callback) + boton_enviar.pack(pady=5) + + # Botón para desconectar + boton_desconectar = tk.Button(raiz, text="Desconectar", command=desconectar) + boton_desconectar.pack(pady=5) + + # Iniciar el bucle de eventos de la interfaz gráfica + raiz.mainloop() + + +def enviar_nombre(nombre, entrada_nombre, boton_enviar): + global conectado + try: + if conectado: + # ENVIAR NOMBRE + socket_cliente.send(nombre.encode()) + + #RESPUESTA SERVIDOR + respuesta = socket_cliente.recv(1024).decode() + print(respuesta) + + # DESHABILITAR + entrada_nombre.config(state=tk.DISABLED) + boton_enviar.config(state=tk.DISABLED) + except Exception as e: + print("Error:", e) + conectado = False + +def desconectar(): + global conectado + try: + if conectado: + # NOTIFICAR AL SERVIDOR + socket_cliente.send("desconectar".encode()) + + # CERRAR SOCKET + socket_cliente.close() + conectado = False + except Exception as e: + print("Error al desconectar:", e) + + +# BLOQUE DE INICIALIZACION +if __name__ == "__main__": + principal() diff --git a/servidorUsuariosActivos.py b/servidorUsuariosActivos.py new file mode 100644 index 0000000..b8f995b --- /dev/null +++ b/servidorUsuariosActivos.py @@ -0,0 +1,125 @@ +import socket +import threading +import time +import tkinter as tk + +#LISTA USUARIOS +lista_usuarios = None + +def iniciar_servidor(host, puerto): + global lista_usuarios + + #SOCKET + socket_servidor = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + socket_servidor.bind((host, puerto)) + + #ESCUCHA + socket_servidor.listen(1) + print(f"SERVIDOR INICIADO EN LA DIRECCION {host}:{puerto}") + + # DICCIONARIO + clientes = {} + + # BUCLE INFINITO PARA ACCEPTAR CONEXIONES + while True: + # ACCEPTAR CONEXION + socket_cliente, direccion = socket_servidor.accept() + + # CREAR UN NUEVO HILO POR CADA CLIENTE + hilo_cliente = threading.Thread(target=manejar_cliente, args=(socket_cliente, clientes)) + hilo_cliente.start() + + +def manejar_cliente(socket_cliente, clientes): + + # NOMBRE CLIENTE + nombre = socket_cliente.recv(1024).decode() + + # AGREGAMOS CLIENTES AL DICCIONARIO CON UN NOMBRE + clientes[socket_cliente] = nombre + + print(f"{nombre} conectado desde {socket_cliente.getpeername()}") + + # Bucle infinito para manejar la interacción con el cliente + while True: + try: + # Espera 5 segundos + time.sleep(5) + + # Obtiene el número de clientes conectados + clientes_conectados = len(clientes) + + # Actualiza la lista de usuarios en la interfaz gráfica + actualizar_lista_usuarios(clientes) + + # Actualiza el contador en la GUI del servidor + actualizar_contador_usuarios(clientes_conectados) + + # Construye el mensaje con el número de clientes conectados + mensaje = f"CANTIDAD DE CLIENTES CONECTADOS: {clientes_conectados}" + + # Envía el mensaje al cliente actual + socket_cliente.send(mensaje.encode()) + + except Exception as e: + print(e) + # Maneja cualquier excepción eliminando al cliente del diccionario y cerrando su socket + if socket_cliente in clientes: + del clientes[socket_cliente] + socket_cliente.close() + # Envía un mensaje a los clientes restantes informando que este cliente se ha desconectado + for cliente in clientes: + cliente.send(f"{nombre} DESCONECTADO".encode()) + break + +def actualizar_lista_usuarios(clientes): + global lista_usuarios + # Obtener los nombres de usuario de los clientes conectados + usuarios_conectados = [nombre for cliente, nombre in clientes.items()] + + # Limpiar la lista de usuarios en la interfaz gráfica + lista_usuarios.delete(0, tk.END) + + # Agregar los usuarios conectados a la lista de usuarios en la interfaz gráfica + for usuario in usuarios_conectados: + lista_usuarios.insert(tk.END, usuario) + +def actualizar_contador_usuarios(contador): + global contador_usuarios + contador_usuarios.config(text=str(contador)) + + +def generar_interfaz(): + global lista_usuarios + global contador_usuarios + + # VENTANA + ventana = tk.Tk() + ventana.title("Estado del servidor") + + # CONTADOR + contador_usuarios = tk.Label(ventana, text="0", font=("Arial", 72), fg="green") + contador_usuarios.pack(pady=50) + + # TITULO + titulo_nombre_clientes = tk.Label(ventana, text="NOMBRE CLIENTES CONECTADOS", font=("Arial", 18)) + titulo_nombre_clientes.pack() + + #LISTA USUARIOS ACTIVOS + lista_usuarios = tk.Listbox(ventana) + lista_usuarios.pack() + + #ESPERAR EVENTOS + ventana.mainloop() + +# Bloque principal +if __name__ == "__main__": + # Definición del host y puerto del servidor + HOST = 'localhost' + PUERTO = 8088 + + # Inicia el servidor en un hilo de ejecución separado + threading.Thread(target=iniciar_servidor, args=(HOST, PUERTO)).start() + + # Crea la interfaz gráfica en un hilo de ejecución separado + threading.Thread(target=generar_interfaz).start()