""" FTP Server - Main server class that handles multiple concurrent connections """ import socket import threading import os import signal import sys from config import FTP_HOST, FTP_PORT, SERVER_ROOT from ftp_session import FTPSession class FTPServer: def __init__(self, host=FTP_HOST, port=FTP_PORT): self.host = host self.port = port self.server_socket = None self.running = False self.client_threads = [] # Ensure server root directory exists if not os.path.exists(SERVER_ROOT): os.makedirs(SERVER_ROOT) print(f"Created server root directory: {SERVER_ROOT}") # Setup signal handlers for graceful shutdown signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGTERM, self.signal_handler) def signal_handler(self, signum, frame): """Handle shutdown signals""" print(f"\nReceived signal {signum}, shutting down server...") self.stop_server() sys.exit(0) def start_server(self): """Start the FTP server""" try: # Create server socket self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Bind to address and port self.server_socket.bind((self.host, self.port)) self.server_socket.listen(5) self.running = True print(f"FTP Server started on {self.host}:{self.port}") print(f"Server root directory: {os.path.abspath(SERVER_ROOT)}") print("Waiting for connections...") while self.running: try: # Accept client connection client_socket, client_address = self.server_socket.accept() print(f"New connection from {client_address}") # Create new thread for client client_thread = threading.Thread( target=self.handle_client, args=(client_socket, client_address), daemon=True ) client_thread.start() self.client_threads.append(client_thread) # Clean up finished threads self.cleanup_threads() except OSError: if self.running: print("Error accepting connection") break except Exception as e: print(f"Error in server loop: {e}") break except Exception as e: print(f"Error starting server: {e}") finally: self.stop_server() def handle_client(self, client_socket, client_address): """Handle individual client connection""" try: # Create FTP session for this client session = FTPSession(client_socket, client_address) session.handle_session() except Exception as e: print(f"Error handling client {client_address}: {e}") finally: try: client_socket.close() except: pass def cleanup_threads(self): """Remove finished threads from the list""" self.client_threads = [t for t in self.client_threads if t.is_alive()] def stop_server(self): """Stop the FTP server""" if not self.running: return print("Stopping FTP server...") self.running = False # Close server socket if self.server_socket: try: self.server_socket.close() except: pass # Wait for client threads to finish (with timeout) for thread in self.client_threads: if thread.is_alive(): thread.join(timeout=2.0) print("FTP server stopped") def get_server_info(self): """Get server information""" return { 'host': self.host, 'port': self.port, 'running': self.running, 'active_connections': len([t for t in self.client_threads if t.is_alive()]), 'server_root': os.path.abspath(SERVER_ROOT) } def main(): """Main function to start the FTP server""" print("=" * 50) print("FTP Server - Computer Networks Design Project") print("=" * 50) # Create and start server server = FTPServer() try: server.start_server() except KeyboardInterrupt: print("\nShutdown requested by user") except Exception as e: print(f"Server error: {e}") finally: server.stop_server() if __name__ == "__main__": main()