156 lines
4.9 KiB
Python
156 lines
4.9 KiB
Python
"""
|
|
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()
|