commit ebb7ed59fcffbb8ef6dd7c845fab6338b70d9360 Author: Eric Date: Fri Feb 16 17:15:36 2024 +0100 Update diff --git a/Cliente.py b/Cliente.py new file mode 100644 index 0000000..ea72d80 --- /dev/null +++ b/Cliente.py @@ -0,0 +1,357 @@ +import tkinter as tk +from tkinter import ttk, filedialog +import os +import socket +import threading +import time + +class FTPClient: + def __init__(self): + self.server_dir = "" + self.root = tk.Tk() + self.root.title("Cliente de Archivos") + self.root.geometry("950x600") + + # Valores predeterminados para la dirección IP y el puerto + default_ip = "127.0.0.1" + default_port = "12345" + + # Frame para la conexión + self.connection_frame = tk.Frame(self.root) + self.connection_frame.pack(pady=10) + + self.con_frame = tk.LabelFrame(self.connection_frame, text="Conexion") + self.con_frame.grid(row=0, column=0, padx=10, pady=5) + + self.label_ip = tk.Label(self.con_frame, text="IP del Servidor:") + self.label_ip.grid(row=0, column=0, padx=5) + + self.entry_ip = tk.Entry(self.con_frame, width=20) + self.entry_ip.insert(0, default_ip) + self.entry_ip.grid(row=0, column=1, padx=5) + + self.label_port = tk.Label(self.con_frame, text="Puerto:") + self.label_port.grid(row=0, column=2, padx=5) + + self.entry_port = tk.Entry(self.con_frame, width=10) + self.entry_port.insert(0, default_port) + self.entry_port.grid(row=0, column=3, padx=5) + + self.button_connect = tk.Button(self.con_frame, text="Conectar", width=10, command=self.connect_to_server) + self.button_connect.grid(row=0, column=4, padx=5) + + self.button_disconnect = tk.Button(self.con_frame, text="Desconectar", width=10, state=tk.DISABLED, command=self.disconnect_from_server) + self.button_disconnect.grid(row=0, column=5, padx=5) + + # Frame principal para los archivos locales y directorios del servidor + self.main_frame = tk.Frame(self.root) + self.main_frame.pack(fill="both", expand=True, padx=20, pady=5) + + # Frame para la configuración de la carpeta local del cliente + self.config_frame = tk.LabelFrame(self.main_frame, text="Configuración de Carpeta Local") + self.config_frame.pack(fill="x", expand=True, padx=20, pady=5, anchor="center") # Alinear al centro + + self.label_local_folder = tk.Label(self.config_frame, text="Carpeta Local:") + self.label_local_folder.grid(row=0, column=0, padx=20, sticky="e") + + self.entry_local_folder = tk.Entry(self.config_frame, width=50) + self.entry_local_folder.grid(row=0, column=1, padx=20, sticky="w") + + self.button_browse = tk.Button(self.config_frame, text="Explorar", width=10, command=self.browse_local_folder) + self.button_browse.grid(row=0, column=2, padx=20, pady=10, sticky="e") + + self.button_apply = tk.Button(self.config_frame, text="Aplicar", width=10, command=self.apply_local_folder) + self.button_apply.grid(row=0, column=3, padx=5, pady=10, sticky="w") + + # Frame para los botones de navegación de archivos locales + self.local_nav_frame = tk.Frame(self.main_frame) + self.local_nav_frame.pack(side="left", padx=5) + + self.button_up = tk.Button(self.local_nav_frame, text="Subir", command=self.go_up) + self.button_up.pack(side="top", pady=5) + + self.button_open = tk.Button(self.local_nav_frame, text="Abrir", command=self.open_directory) + self.button_open.pack(side="top", pady=5) + + # Frame para los botones de navegación de archivos del servidor + self.server_nav_frame = tk.Frame(self.main_frame) + self.server_nav_frame.pack(side="right", padx=5) + + self.server_button_up = tk.Button(self.server_nav_frame, text="Subir", command=self.go_up_server) + self.server_button_up.pack(side="top", pady=5) + + self.server_button_open = tk.Button(self.server_nav_frame, text="Abrir", command=self.open_directory_server) + self.server_button_open.pack(side="top", pady=5) + + # Frame para todos los archivos + self.files_frame = tk.Frame(self.main_frame) + self.files_frame.pack(fill="both", expand=True, padx=20, pady=5) + + # Frame para los archivos locales + self.local_files_frame = tk.LabelFrame(self.files_frame, text="Archivos Locales") + self.local_files_frame.pack(side="left", fill="both", expand=True) + + self.local_files_list = tk.Listbox(self.local_files_frame, width=50, height=20) + self.local_files_list.pack(side="left", fill="both", expand=True) + + self.local_files_scrollbar = ttk.Scrollbar(self.local_files_frame, orient="vertical") + self.local_files_scrollbar.config(command=self.local_files_list.yview) + self.local_files_scrollbar.pack(side="right", fill="y") + + self.local_files_list.config(yscrollcommand=self.local_files_scrollbar.set) + + # Frame para los archivos del servidor + self.server_files_frame = tk.LabelFrame(self.files_frame, text="Archivos del Servidor") + self.server_files_frame.pack(side="right", fill="both", expand=True) + + self.server_files_list = tk.Listbox(self.server_files_frame, width=50, height=20) + self.server_files_list.pack(side="left", fill="both", expand=True) + + self.server_files_scrollbar = ttk.Scrollbar(self.server_files_frame, orient="vertical") + self.server_files_scrollbar.config(command=self.server_files_list.yview) + self.server_files_scrollbar.pack(side="right", fill="y") + + self.server_files_list.config(yscrollcommand=self.server_files_scrollbar.set) + + # Frame para el botón y la ProgressBar + self.upload_frame = tk.Frame(self.main_frame) + self.upload_frame.pack(side="left", fill="both", pady=10) + + self.button_send_file = tk.Button(self.upload_frame, text="Enviar Archivo", command=self.send_selected_file) + self.button_send_file.pack(side="left", padx=10) + + self.entry_server_path = tk.Entry(self.upload_frame, width=50) + self.entry_server_path.pack(side="left", pady=5) + + self.progress_bar = ttk.Progressbar(self.upload_frame, orient="horizontal", length=200, mode="determinate") + self.progress_bar.pack(side="right", padx=10) + + self.timer_label = tk.Label(self.upload_frame, text="") + self.timer_label.pack() + + self.client_socket = None + self.monitor_thread = None # Hilo para monitorear el buzón de entrada + + def connect_to_server(self): + server = self.entry_ip.get() + port = int(self.entry_port.get()) + self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + print("Conectando al servidor...") + self.client_socket.settimeout(10) # Establecer un tiempo de espera de 10 segundos para la respuesta del servidor + self.client_socket.connect((server, port)) + credentials = "user:password" + self.client_socket.send(credentials.encode()) + print(f"Conectado al servidor {server}:{port}") + + # Esperar y recibir el mensaje de bienvenida del servidor + response = self.receive_response(self.client_socket) + print("Mensaje de bienvenida del servidor:", response.decode("utf-8")) + + # Si el mensaje indica que el servidor está listo, continuar con la configuración + if response.startswith(b"pyftpdlib"): + # Enviar comando para obtener el directorio del servidor + self.send_data(b"PWD", self.client_socket) + server_dir_response = self.receive_response(self.client_socket) + print("Respuesta del servidor al solicitar el directorio:", server_dir_response.decode("utf-8")) + default_server_dir = server_dir_response.decode("utf-8").split('"')[1] # Extraer el directorio del servidor de la respuesta + print(f"Directorio predeterminado del servidor: {default_server_dir}") + self.update_default_server_dir(default_server_dir) # Actualizar la interfaz con el directorio predeterminado del servidor + # Restaurar el tiempo de espera del socket a 5 segundos + self.client_socket.settimeout(5) + self.button_connect.config(state=tk.DISABLED) + self.button_disconnect.config(state=tk.NORMAL) + self.monitor_thread = threading.Thread(target=self.monitor_inbox) + self.monitor_thread.start() # Iniciar el hilo de monitoreo del buzón de entrada + else: + print("Respuesta inesperada del servidor:", response.decode("utf-8")) + except socket.timeout: + print("Conexión agotada. Por favor, verifique la IP y el puerto del servidor.") + except Exception as e: + print(f"Error al conectar al servidor: {e}") + + def disconnect_from_server(self): + if self.client_socket: + self.client_socket.close() + print("Disconnected from server.") + self.button_connect.config(state=tk.NORMAL) + self.button_disconnect.config(state=tk.DISABLED) + + def send_selected_file(self): + selected_file = self.local_files_list.get(tk.ACTIVE) + full_path = os.path.join(self.entry_local_folder.get(), selected_file) + if os.path.isfile(full_path): + recipient = "" # Introduce el destinatario si es necesario + threading.Thread(target=self.upload_file, args=(full_path, recipient)).start() # Ejecutar la función de carga en un hilo separado + else: + print("Please select a file to send.") + + def upload_file(self, filename, recipient): + try: + with open(filename, 'rb') as file: + self.send_data(b'SEND_FILE', self.client_socket) + self.send_data(recipient.encode('utf-8'), self.client_socket) + self.send_file(filename, self.client_socket) + print(f"File '{filename}' uploaded successfully to {recipient}") + except Exception as e: + print(f"Error uploading file: {e}") + + def send_file(self, filename, client_socket): + with open(filename, 'rb') as file: + filename_bytes = os.path.basename(filename).encode('utf-8') + self.send_data(filename_bytes, client_socket) + file_size = os.path.getsize(filename) + self.send_data(str(file_size).encode('utf-8'), client_socket) + sent_bytes = 0 + while True: + chunk = file.read(1024) + if not chunk: + break + client_socket.sendall(chunk) + sent_bytes += len(chunk) + # Actualizar la ProgressBar + progress = min(int(sent_bytes / file_size * 100), 100) + self.progress_bar['value'] = progress + self.root.update_idletasks() # Actualizar la interfaz gráfica + + def monitor_inbox(self): + while True: + self.send_data(b'CHECK_INBOX', self.client_socket) + data = self.receive_response(self.client_socket) + if data: + filename = data.decode('utf-8') + self.receive_file(filename) + time.sleep(300) # Check every 5 minutes + + def receive_file(self, filename): + try: + with open(filename, 'wb') as file: + filename_bytes = filename.encode('utf-8') + self.send_data(b'RETRIEVE_FILE', self.client_socket) + self.send_data(filename_bytes, self.client_socket) + file_size = int(self.receive_response(self.client_socket).decode('utf-8')) + received_bytes = 0 + while received_bytes < file_size: + chunk = self.client_socket.recv(1024) + if not chunk: + break + file.write(chunk) + received_bytes += len(chunk) + print(f"File '{filename}' downloaded successfully") + except Exception as e: + print(f"Error receiving file: {e}") + + def receive_response(self, client_socket): + try: + print("Esperando respuesta del servidor...") + response_data = b'' # Inicializar un buffer para almacenar los datos recibidos + + # Recibir los datos hasta que se encuentre el delimitador especial + while True: + chunk = client_socket.recv(1024) + if not chunk: + break + response_data += chunk + if b'\n' in chunk: + break + + response_str = response_data # Devolver los datos recibidos tal cual + print(f"Respuesta del servidor recibida: {response_str}") + + return response_str + except Exception as e: + print(f"Error al recibir respuesta del servidor: {e}") + return None + + + def send_data(self, data, client_socket): + data_size = len(data) + data_size_bytes = data_size.to_bytes(4, byteorder='big') + client_socket.sendall(data_size_bytes) + total_sent = 0 + while total_sent < data_size: + sent = client_socket.send(data[total_sent:]) + if sent == 0: + raise RuntimeError("Connection closed unexpectedly.") + total_sent += sent + + def browse_local_folder(self): + folder_path = filedialog.askdirectory() + self.entry_local_folder.delete(0, tk.END) + self.entry_local_folder.insert(0, folder_path) + self.update_local_files_list() + + def apply_local_folder(self): + folder_path = self.entry_local_folder.get() + if os.path.isdir(folder_path): + self.update_local_files_list() + else: + print("Invalid directory path.") + + def go_up(self): + current_path = self.entry_local_folder.get() + parent_path = os.path.dirname(current_path) + if os.path.isdir(parent_path): + self.entry_local_folder.delete(0, tk.END) + self.entry_local_folder.insert(0, parent_path) + self.update_local_files_list() + + def open_directory(self): + selected_path = self.local_files_list.get(tk.ACTIVE) + full_path = os.path.join(self.entry_local_folder.get(), selected_path) + if os.path.isdir(full_path): + self.entry_local_folder.delete(0, tk.END) + self.entry_local_folder.insert(0, full_path) + self.update_local_files_list() + + def go_up_server(self): + current_path = self.entry_server_folder.get() + parent_path = os.path.dirname(current_path) + if parent_path: + self.entry_server_folder.delete(0, tk.END) + self.entry_server_folder.insert(0, parent_path) + self.update_server_files_list() + + def open_directory_server(self): + selected_path = self.server_files_list.get(tk.ACTIVE) + full_path = os.path.join(self.entry_server_folder.get(), selected_path) + if os.path.isdir(full_path): + self.entry_server_folder.delete(0, tk.END) + self.entry_server_folder.insert(0, full_path) + self.update_server_files_list() + + def update_local_files_list(self): + folder_path = self.entry_local_folder.get() + self.local_files_list.delete(0, tk.END) + if os.path.isdir(folder_path): + files = os.listdir(folder_path) + for file in files: + self.local_files_list.insert(tk.END, file) + + def update_default_server_dir(self, directory): + # Actualizar el atributo del directorio del servidor + self.server_dir = directory + + # Limpiar la lista de archivos del servidor + self.server_files_list.delete(0, tk.END) + + # Obtener la lista de archivos en el nuevo directorio del servidor + self.send_data(b'LIST_FILES', self.client_socket) + self.send_data(directory.encode('utf-8'), self.client_socket) + files_data = self.receive_response(self.client_socket) + + # Decodificar los datos recibidos y agregar cada archivo a la lista + files = files_data.decode('utf-8').split('\n') + for file in files: + self.server_files_list.insert(tk.END, file) + + + def run(self): + self.root.mainloop() + +def main(): + client = FTPClient() + client.run() + +if __name__ == "__main__": + main() diff --git a/README.md b/README.md new file mode 100644 index 0000000..03a641f --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Cliente y Servidor de Envío de Archivos + +Este proyecto consiste en una aplicación cliente-servidor desarrollada en Python que permite a los usuarios enviar archivos de un cliente a un servidor. + +## Cliente + +El cliente de la aplicación está diseñado con una interfaz gráfica simple usando la biblioteca Tkinter de Python. Permite al usuario conectarse al servidor, explorar archivos locales y enviar archivos seleccionados al servidor. + +### Características del Cliente: + +- **Conexión al Servidor:** El cliente puede conectarse al servidor proporcionando la dirección IP y el puerto. +- **Exploración de Archivos Locales:** Se puede navegar a través de los archivos locales del cliente para seleccionar un archivo para enviar al servidor. +- **Envío de Archivos:** Una vez que se selecciona un archivo, el cliente puede enviarlo al servidor. Muestra el progreso del envío con una barra de progreso. + +### Requisitos del Cliente: +- Python 3.x instalado. +- Biblioteca Tkinter (normalmente incluida con la instalación estándar de Python). + +## Servidor + +El servidor es una aplicación de consola simple que espera conexiones de clientes. Cuando un cliente se conecta, el servidor recibe el archivo enviado por el cliente y lo guarda en una carpeta designada. + +### Características del Servidor: + +- **Espera de Conexiones:** El servidor espera a que los clientes se conecten en un puerto especificado. +- **Recepción de Archivos:** Cuando un cliente se conecta y envía un archivo, el servidor lo recibe y lo guarda en la carpeta designada. +- **Creación de Carpetas de Usuario:** Si la carpeta de usuario correspondiente no existe en el servidor, el servidor la crea automáticamente al recibir la primera conexión de ese usuario. + +### Requisitos del Servidor: +- Python 3.x instalado. + +## Uso + +### Cliente: +1. Ejecute el script `Cliente.py`. +2. Ingrese la dirección IP y el puerto del servidor al que desea conectarse. +3. Haga clic en el botón "Conectar". +4. Use el botón "Buscar Archivos" para explorar los archivos locales y seleccione el archivo que desea enviar. +5. Haga clic en el botón "Enviar Archivo" para enviar el archivo seleccionado al servidor. + +### Servidor: +1. Ejecute el script `Servidor.py`. +2. El servidor comenzará a esperar conexiones de clientes en el puerto especificado. +3. Los archivos enviados por los clientes se guardarán en la carpeta `usuarios` en el servidor, en una subcarpeta correspondiente al cliente. + +## Notas Adicionales + +- Asegúrese de que el cliente y el servidor estén en la misma red para que puedan comunicarse correctamente. +- Siempre verifique las configuraciones de firewall y permisos de red para permitir la comunicación entre el cliente y el servidor. diff --git a/Servidor.py b/Servidor.py new file mode 100644 index 0000000..5a942ad --- /dev/null +++ b/Servidor.py @@ -0,0 +1,72 @@ +from pyftpdlib.authorizers import DummyAuthorizer +from pyftpdlib.handlers import FTPHandler +from pyftpdlib.servers import FTPServer +import os +import time +import string + +class CustomFTPHandler(FTPHandler): + def on_connect(self): + super().on_connect() + self.username = "user" + print("Cliente conectado.") + + def on_login(self, username): + super().on_login(username) + self.username = username + self.user_dir = os.path.join(self.authorizer.get_home_dir(username), 'inbox') + + # Send the default server directory path to the client + default_server_dir = "/home/eric/ProyectoJuanjoSegundoTrimestre/usuario" # Cambiar esto al directorio predeterminado + self.send_response("DEFAULT_DIR", default_server_dir) + print(f"Directorio predeterminado del servidor enviado al cliente: {default_server_dir}") + + def send_response(self, code, message): + resp = f"{code} {message}\r\n" + self.push(resp) + + def on_file_sent(self, file): + super().on_file_sent(file) + self.broadcast_file_sent(os.path.basename(file)) + + def broadcast_file_sent(self, filename): + print(f"File '{filename}' sent") + + def list_files_in_user_inbox(self): + files = [] + try: + files = os.listdir(self.user_dir) + except FileNotFoundError: + pass + return files + + def normalize_filename(self, filename): + valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits) + filename = ''.join(c for c in filename if c in valid_chars) + filename = filename[:255] # Limit filename length to 255 characters + return filename + + def save_file(self, filename, data): + try: + filename = self.normalize_filename(filename) + with open(os.path.join(self.user_dir, filename), 'wb') as file: + file.write(data) + print(f"File '{filename}' saved to {self.username}'s inbox.") + except Exception as e: + print(f"Error saving file: {e}") + +def run_ftp_server(): + authorizer = DummyAuthorizer() + # Creamos un único usuario + authorizer.add_user("user", "password", "/home/eric/ProyectoJuanjoSegundoTrimestre/usuario", perm="lradw") # Cambia "/path/to/home/directory" al directorio que desees + + handler = CustomFTPHandler + handler.authorizer = authorizer + + server = FTPServer(("127.0.0.1", 12345), handler) + server.max_cons_per_ip = 5 + + server.serve_forever() + +if __name__ == "__main__": + run_ftp_server() diff --git a/usuario/127.0.0.1/Nuevo Documento de texto.txt b/usuario/127.0.0.1/Nuevo Documento de texto.txt new file mode 100644 index 0000000..e69de29