fix: start_server.py — исправлено завершение по Ctrl+C

Проблемы:
- signal_handler блокировал shutdown()
- Порт не освобождался после Ctrl+C
- Невозможно было перезапустить сервер

Решения:
- Убран signal_handler — используется KeyboardInterrupt
- shutdown() вызывается в отдельном потоке (avoid deadlock)
- with контекст для автоматического закрытия
- allow_reuse_address = True для быстрого рестарта
- Обработка OSError при занятом порте
This commit is contained in:
2026-05-21 13:19:59 +03:00
parent 0776d839b9
commit ed6ecc548b
+30 -29
View File
@@ -13,16 +13,13 @@ import argparse
import http.server import http.server
import socketserver import socketserver
import os import os
import signal
import sys import sys
import threading
from pathlib import Path from pathlib import Path
PORT = 8080 PORT = 8080
SCRIPT_DIR = Path(__file__).parent.resolve() SCRIPT_DIR = Path(__file__).parent.resolve()
# Global server reference for cleanup
httpd = None
class CustomHandler(http.server.SimpleHTTPRequestHandler): class CustomHandler(http.server.SimpleHTTPRequestHandler):
"""Custom handler with better logging.""" """Custom handler with better logging."""
@@ -36,32 +33,20 @@ class CustomHandler(http.server.SimpleHTTPRequestHandler):
pass pass
def signal_handler(sig, frame): class ReusableTCPServer(socketserver.TCPServer):
"""Handle Ctrl+C gracefully.""" """TCP Server with address reuse enabled."""
print("\n👋 Shutting down...") allow_reuse_address = True
if httpd:
# Use shutdown() to stop serve_forever()
httpd.shutdown()
def main(): def main():
global httpd
parser = argparse.ArgumentParser(description='WebSocket Client Web Server') parser = argparse.ArgumentParser(description='WebSocket Client Web Server')
parser.add_argument('--port', type=int, default=PORT, help=f'Port (default: {PORT})') parser.add_argument('--port', type=int, default=PORT, help=f'Port (default: {PORT})')
parser.add_argument('--host', default='0.0.0.0', help='Host (default: 0.0.0.0)') parser.add_argument('--host', default='0.0.0.0', help='Host (default: 0.0.0.0)')
args = parser.parse_args() args = parser.parse_args()
# Change to websocket-service directory # Change to script directory
os.chdir(SCRIPT_DIR) os.chdir(SCRIPT_DIR)
# Register signal handlers
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# Allow socket reuse
socketserver.TCPServer.allow_reuse_address = True
print(f""" print(f"""
╔══════════════════════════════════════════════════════════╗ ╔══════════════════════════════════════════════════════════╗
║ 🛰️ Wyoming Satellite WebSocket Client Server ║ ║ 🛰️ Wyoming Satellite WebSocket Client Server ║
@@ -74,16 +59,32 @@ def main():
╚══════════════════════════════════════════════════════════╝ ╚══════════════════════════════════════════════════════════╝
""") """)
httpd = socketserver.TCPServer((args.host, args.port), CustomHandler)
print(f"🚀 Server started on http://{args.host}:{args.port}")
try: try:
httpd.serve_forever() with ReusableTCPServer((args.host, args.port), CustomHandler) as httpd:
except KeyboardInterrupt: print(f"🚀 Server started on http://{args.host}:{args.port}")
pass print("📖 Serving files from:", SCRIPT_DIR)
finally: print("\n⌨️ Press Ctrl+C to stop\n")
print("\n👋 Server stopped")
httpd.server_close() try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\n👋 Shutting down gracefully...")
# Shutdown in a separate thread to avoid deadlock
shutdown_thread = threading.Thread(target=httpd.shutdown)
shutdown_thread.start()
shutdown_thread.join(timeout=2)
httpd.server_close()
print("✅ Server stopped successfully")
except OSError as e:
print(f"\n❌ Error: Could not bind to port {args.port}")
print(f" {e}")
print("\n💡 Try using a different port:")
print(f" python3 {sys.argv[0]} --port 8081\n")
sys.exit(1)
except Exception as e:
print(f"\n❌ Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":