""" 客户端管理器模块 负责管理客户端连接、认证和断开 """ import time import threading import uuid from typing import Dict, Any, List, Optional class ClientManager: """ 客户端管理器 负责管理客户端连接、认证和断开 """ def __init__(self, plugin): """ 初始化客户端管理器 Args: plugin: 服务器架构插件实例 """ self.plugin = plugin self.enabled = False self.initialized = False # 客户端配置 self.client_config = { "enable_authentication": True, "auth_timeout": 30.0, "max_clients": 1000, "enable_heartbeat": True, "heartbeat_interval": 30.0, "enable_rate_limiting": True, "max_requests_per_second": 100, "enable_banning": True, "ban_duration": 3600 # 1小时 } # 客户端存储 self.clients = {} # 已认证客户端 self.pending_clients = {} # 待认证客户端 self.banned_clients = {} # 被禁止的客户端 # 客户端统计 self.client_stats = { "total_clients": 0, "authenticated_clients": 0, "pending_clients": 0, "disconnected_clients": 0, "banned_clients": 0, "authentication_success": 0, "authentication_failed": 0, "rate_limit_exceeded": 0, "heartbeat_timeouts": 0, "errors": 0 } # 线程锁 self.clients_lock = threading.RLock() # 回调函数 self.client_callbacks = { "client_authenticated": [], "client_disconnected": [], "client_banned": [], "authentication_failed": [], "rate_limit_exceeded": [] } # 时间戳记录 self.last_cleanup = 0.0 self.last_heartbeat_check = 0.0 print("✓ 客户端管理器已创建") def initialize(self) -> bool: """ 初始化客户端管理器 Returns: 是否初始化成功 """ try: print("正在初始化客户端管理器...") self.initialized = True print("✓ 客户端管理器初始化完成") return True except Exception as e: print(f"✗ 客户端管理器初始化失败: {e}") self.client_stats["errors"] += 1 import traceback traceback.print_exc() return False def enable(self) -> bool: """ 启用客户端管理器 Returns: 是否启用成功 """ try: if not self.initialized: print("✗ 客户端管理器未初始化") return False self.enabled = True print("✓ 客户端管理器已启用") return True except Exception as e: print(f"✗ 客户端管理器启用失败: {e}") self.client_stats["errors"] += 1 import traceback traceback.print_exc() return False def disable(self): """禁用客户端管理器""" try: self.enabled = False # 断开所有客户端 self.disconnect_all_clients() print("✓ 客户端管理器已禁用") except Exception as e: print(f"✗ 客户端管理器禁用失败: {e}") self.client_stats["errors"] += 1 import traceback traceback.print_exc() def finalize(self): """清理客户端管理器资源""" try: # 禁用管理器 if self.enabled: self.disable() # 清理回调 self.client_callbacks.clear() self.initialized = False print("✓ 客户端管理器资源已清理") except Exception as e: print(f"✗ 客户端管理器资源清理失败: {e}") import traceback traceback.print_exc() def update(self, dt: float): """ 更新客户端管理器状态 Args: dt: 时间增量(秒) """ try: if not self.enabled: return current_time = time.time() # 定期清理过期数据 if current_time - self.last_cleanup > 60.0: # 每分钟清理一次 self._cleanup_expired_data(current_time) self.last_cleanup = current_time # 检查心跳超时 if self.client_config["enable_heartbeat"]: if current_time - self.last_heartbeat_check > self.client_config["heartbeat_interval"]: self._check_heartbeat_timeouts(current_time) self.last_heartbeat_check = current_time except Exception as e: print(f"✗ 客户端管理器更新失败: {e}") self.client_stats["errors"] += 1 import traceback traceback.print_exc() def _cleanup_expired_data(self, current_time: float): """清理过期数据""" try: # 清理过期的待认证客户端 expired_pending = [] for client_id, client_data in self.pending_clients.items(): if current_time - client_data["connect_time"] > self.client_config["auth_timeout"]: expired_pending.append(client_id) for client_id in expired_pending: del self.pending_clients[client_id] self.client_stats["pending_clients"] -= 1 # 清理过期的禁止列表 expired_bans = [] for client_addr, ban_data in self.banned_clients.items(): if current_time - ban_data["ban_time"] > self.client_config["ban_duration"]: expired_bans.append(client_addr) for client_addr in expired_bans: del self.banned_clients[client_addr] self.client_stats["banned_clients"] -= 1 except Exception as e: print(f"✗ 过期数据清理失败: {e}") self.client_stats["errors"] += 1 def _check_heartbeat_timeouts(self, current_time: float): """检查心跳超时""" try: timed_out_clients = [] with self.clients_lock: for client_id, client_data in self.clients.items(): last_heartbeat = client_data.get("last_heartbeat", client_data["connect_time"]) if current_time - last_heartbeat > self.client_config["heartbeat_interval"] * 3: timed_out_clients.append(client_id) for client_id in timed_out_clients: self.disconnect_client(client_id, "heartbeat_timeout") self.client_stats["heartbeat_timeouts"] += 1 except Exception as e: print(f"✗ 心跳超时检查失败: {e}") self.client_stats["errors"] += 1 def add_pending_client(self, client_socket, client_address) -> str: """ 添加待认证客户端 Args: client_socket: 客户端套接字 client_address: 客户端地址 Returns: 客户端ID """ try: # 检查是否被禁止 if self._is_client_banned(client_address): client_socket.close() self.client_stats["banned_clients"] += 1 return "" # 检查连接限制 if len(self.pending_clients) + len(self.clients) >= self.client_config["max_clients"]: client_socket.close() return "" # 生成客户端ID client_id = str(uuid.uuid4()) # 添加到待认证列表 with self.clients_lock: self.pending_clients[client_id] = { "socket": client_socket, "address": client_address, "connect_time": time.time(), "last_activity": time.time(), "request_count": 0, "last_request_time": time.time() } self.client_stats["pending_clients"] += 1 return client_id except Exception as e: print(f"✗ 添加待认证客户端失败: {e}") self.client_stats["errors"] += 1 return "" def authenticate_client(self, client_id: str, auth_data: Dict[str, Any]) -> bool: """ 认证客户端 Args: client_id: 客户端ID auth_data: 认证数据 Returns: 是否认证成功 """ try: if not self.enabled: return False if not self.client_config["enable_authentication"]: # 如果禁用认证,直接通过 return self._complete_authentication(client_id, auth_data) # 检查客户端是否在待认证列表中 if client_id not in self.pending_clients: self.client_stats["authentication_failed"] += 1 return False # 使用安全管理系统进行认证 if self.plugin.security_manager: auth_result = self.plugin.security_manager.authenticate_client( client_id, auth_data, self.pending_clients[client_id]["address"] ) if auth_result: return self._complete_authentication(client_id, auth_data) else: self.client_stats["authentication_failed"] += 1 # 触发认证失败回调 self._trigger_client_callback("authentication_failed", { "client_id": client_id, "auth_data": auth_data, "timestamp": time.time() }) # 移除待认证客户端 with self.clients_lock: if client_id in self.pending_clients: del self.pending_clients[client_id] self.client_stats["pending_clients"] -= 1 return False else: # 如果没有安全管理系统,使用简单认证 return self._complete_authentication(client_id, auth_data) except Exception as e: print(f"✗ 客户端认证失败: {e}") self.client_stats["errors"] += 1 return False def _complete_authentication(self, client_id: str, auth_data: Dict[str, Any]) -> bool: """ 完成客户端认证 Args: client_id: 客户端ID auth_data: 认证数据 Returns: 是否完成认证 """ try: # 检查客户端是否在待认证列表中 if client_id not in self.pending_clients: return False # 获取待认证客户端数据 pending_client = self.pending_clients[client_id] # 创建已认证客户端数据 client_data = { "socket": pending_client["socket"], "address": pending_client["address"], "connect_time": pending_client["connect_time"], "last_activity": time.time(), "last_heartbeat": time.time(), "user_id": auth_data.get("user_id", ""), "username": auth_data.get("username", ""), "permissions": auth_data.get("permissions", []), "session_id": str(uuid.uuid4()), "bytes_sent": 0, "bytes_received": 0, "messages_sent": 0, "messages_received": 0 } # 移动客户端到已认证列表 with self.clients_lock: self.clients[client_id] = client_data del self.pending_clients[client_id] self.client_stats["authenticated_clients"] += 1 self.client_stats["pending_clients"] -= 1 self.client_stats["authentication_success"] += 1 # 触发客户端认证回调 self._trigger_client_callback("client_authenticated", { "client_id": client_id, "user_id": client_data["user_id"], "username": client_data["username"], "address": client_data["address"], "timestamp": client_data["last_activity"] }) return True except Exception as e: print(f"✗ 完成客户端认证失败: {e}") self.client_stats["errors"] += 1 return False def disconnect_client(self, client_id: str, reason: str = "unknown"): """ 断开客户端连接 Args: client_id: 客户端ID reason: 断开原因 """ try: client_data = None with self.clients_lock: if client_id in self.clients: client_data = self.clients[client_id] del self.clients[client_id] self.client_stats["authenticated_clients"] -= 1 elif client_id in self.pending_clients: client_data = self.pending_clients[client_id] del self.pending_clients[client_id] self.client_stats["pending_clients"] -= 1 if client_data: # 关闭套接字 try: client_data["socket"].close() except: pass self.client_stats["disconnected_clients"] += 1 # 触发客户端断开回调 self._trigger_client_callback("client_disconnected", { "client_id": client_id, "address": client_data["address"], "reason": reason, "timestamp": time.time() }) print(f"✓ 客户端已断开: {client_id} ({reason})") except Exception as e: print(f"✗ 断开客户端连接失败: {e}") self.client_stats["errors"] += 1 def disconnect_all_clients(self): """断开所有客户端连接""" try: with self.clients_lock: # 断开已认证客户端 client_ids = list(self.clients.keys()) for client_id in client_ids: self.disconnect_client(client_id, "server_shutdown") # 断开待认证客户端 pending_ids = list(self.pending_clients.keys()) for client_id in pending_ids: self.disconnect_client(client_id, "server_shutdown") except Exception as e: print(f"✗ 断开所有客户端连接失败: {e}") self.client_stats["errors"] += 1 def ban_client(self, client_address, reason: str = "unknown"): """ 禁止客户端连接 Args: client_address: 客户端地址 reason: 禁止原因 """ try: if not self.client_config["enable_banning"]: return ban_time = time.time() self.banned_clients[client_address] = { "ban_time": ban_time, "reason": reason, "ban_duration": self.client_config["ban_duration"] } self.client_stats["banned_clients"] += 1 # 断开该地址的所有客户端 self._disconnect_clients_by_address(client_address) # 触发客户端禁止回调 self._trigger_client_callback("client_banned", { "address": client_address, "reason": reason, "ban_time": ban_time, "duration": self.client_config["ban_duration"] }) print(f"✓ 客户端已被禁止: {client_address} ({reason})") except Exception as e: print(f"✗ 禁止客户端失败: {e}") self.client_stats["errors"] += 1 def _disconnect_clients_by_address(self, client_address): """ 根据地址断开客户端连接 Args: client_address: 客户端地址 """ try: # 断开已认证客户端 clients_to_disconnect = [] with self.clients_lock: for client_id, client_data in self.clients.items(): if client_data["address"] == client_address: clients_to_disconnect.append(client_id) for client_id in clients_to_disconnect: self.disconnect_client(client_id, "banned") # 断开待认证客户端 pending_to_disconnect = [] with self.clients_lock: for client_id, client_data in self.pending_clients.items(): if client_data["address"] == client_address: pending_to_disconnect.append(client_id) for client_id in pending_to_disconnect: self.disconnect_client(client_id, "banned") except Exception as e: print(f"✗ 根据地址断开客户端失败: {e}") self.client_stats["errors"] += 1 def _is_client_banned(self, client_address) -> bool: """ 检查客户端是否被禁止 Args: client_address: 客户端地址 Returns: 是否被禁止 """ try: if not self.client_config["enable_banning"]: return False if client_address in self.banned_clients: ban_data = self.banned_clients[client_address] if time.time() - ban_data["ban_time"] < ban_data["ban_duration"]: return True else: # 禁止时间已过期,移除 del self.banned_clients[client_address] self.client_stats["banned_clients"] -= 1 return False except Exception as e: print(f"✗ 检查客户端禁止状态失败: {e}") self.client_stats["errors"] += 1 return False def update_client_activity(self, client_id: str): """ 更新客户端活动时间 Args: client_id: 客户端ID """ try: current_time = time.time() with self.clients_lock: if client_id in self.clients: self.clients[client_id]["last_activity"] = current_time elif client_id in self.pending_clients: self.pending_clients[client_id]["last_activity"] = current_time except Exception as e: print(f"✗ 更新客户端活动时间失败: {e}") self.client_stats["errors"] += 1 def update_client_heartbeat(self, client_id: str): """ 更新客户端心跳时间 Args: client_id: 客户端ID """ try: current_time = time.time() with self.clients_lock: if client_id in self.clients: self.clients[client_id]["last_heartbeat"] = current_time except Exception as e: print(f"✗ 更新客户端心跳时间失败: {e}") self.client_stats["errors"] += 1 def send_heartbeat(self): """发送心跳消息到所有客户端""" try: if not self.client_config["enable_heartbeat"]: return heartbeat_message = b'{"type": "heartbeat"}' with self.clients_lock: for client_id, client_data in self.clients.items(): try: client_data["socket"].send(heartbeat_message) client_data["messages_sent"] += 1 client_data["bytes_sent"] += len(heartbeat_message) except Exception as e: print(f"✗ 心跳消息发送失败: {client_id} - {e}") self.client_stats["errors"] += 1 except Exception as e: print(f"✗ 发送心跳消息失败: {e}") self.client_stats["errors"] += 1 def get_client(self, client_id: str) -> Optional[Dict[str, Any]]: """ 获取客户端信息 Args: client_id: 客户端ID Returns: 客户端信息或None """ try: with self.clients_lock: return self.clients.get(client_id, {}).copy() except Exception as e: print(f"✗ 获取客户端信息失败: {e}") self.client_stats["errors"] += 1 return None def get_all_clients(self) -> Dict[str, Dict[str, Any]]: """ 获取所有客户端信息 Returns: 所有客户端信息字典 """ try: with self.clients_lock: return {k: v.copy() for k, v in self.clients.items()} except Exception as e: print(f"✗ 获取所有客户端信息失败: {e}") self.client_stats["errors"] += 1 return {} def get_client_count(self) -> int: """ 获取客户端数量 Returns: 客户端数量 """ try: with self.clients_lock: return len(self.clients) except Exception as e: print(f"✗ 获取客户端数量失败: {e}") self.client_stats["errors"] += 1 return 0 def is_client_authenticated(self, client_id: str) -> bool: """ 检查客户端是否已认证 Args: client_id: 客户端ID Returns: 客户端是否已认证 """ try: with self.clients_lock: return client_id in self.clients except Exception as e: print(f"✗ 检查客户端认证状态失败: {e}") self.client_stats["errors"] += 1 return False def check_rate_limit(self, client_id: str) -> bool: """ 检查客户端速率限制 Args: client_id: 客户端ID Returns: 是否超过速率限制 """ try: if not self.client_config["enable_rate_limiting"]: return False # 未启用速率限制 current_time = time.time() with self.clients_lock: client_data = None if client_id in self.clients: client_data = self.clients[client_id] elif client_id in self.pending_clients: client_data = self.pending_clients[client_id] else: return False # 客户端不存在 # 计算请求速率 time_diff = current_time - client_data["last_request_time"] if time_diff >= 1.0: # 重置计数器 client_data["request_count"] = 0 client_data["last_request_time"] = current_time client_data["request_count"] += 1 # 检查是否超过限制 if client_data["request_count"] > self.client_config["max_requests_per_second"]: self.client_stats["rate_limit_exceeded"] += 1 # 触发速率限制回调 self._trigger_client_callback("rate_limit_exceeded", { "client_id": client_id, "request_count": client_data["request_count"], "max_requests": self.client_config["max_requests_per_second"], "timestamp": current_time }) return True # 超过限制 return False # 未超过限制 except Exception as e: print(f"✗ 检查速率限制失败: {e}") self.client_stats["errors"] += 1 return False def get_client_stats(self) -> Dict[str, Any]: """ 获取客户端统计信息 Returns: 客户端统计字典 """ return { "clients": self.client_stats.copy(), "config": self.client_config.copy(), "current_clients": len(self.clients), "current_pending": len(self.pending_clients), "current_banned": len(self.banned_clients) } def reset_stats(self): """重置客户端统计信息""" try: self.client_stats = { "total_clients": 0, "authenticated_clients": 0, "pending_clients": 0, "disconnected_clients": 0, "banned_clients": 0, "authentication_success": 0, "authentication_failed": 0, "rate_limit_exceeded": 0, "heartbeat_timeouts": 0, "errors": 0 } print("✓ 客户端统计信息已重置") except Exception as e: print(f"✗ 客户端统计信息重置失败: {e}") def set_client_config(self, config: Dict[str, Any]) -> bool: """ 设置客户端配置 Args: config: 客户端配置字典 Returns: 是否设置成功 """ try: self.client_config.update(config) print(f"✓ 客户端配置已更新: {self.client_config}") return True except Exception as e: print(f"✗ 客户端配置设置失败: {e}") return False def get_client_config(self) -> Dict[str, Any]: """ 获取客户端配置 Returns: 客户端配置字典 """ return self.client_config.copy() def _trigger_client_callback(self, callback_type: str, data: Dict[str, Any]): """ 触发客户端回调 Args: callback_type: 回调类型 data: 回调数据 """ try: if callback_type in self.client_callbacks: for callback in self.client_callbacks[callback_type]: try: callback(data) except Exception as e: print(f"✗ 客户端回调执行失败: {callback_type} - {e}") except Exception as e: print(f"✗ 客户端回调触发失败: {e}") def register_client_callback(self, callback_type: str, callback: callable): """ 注册客户端回调 Args: callback_type: 回调类型 callback: 回调函数 """ try: if callback_type in self.client_callbacks: self.client_callbacks[callback_type].append(callback) print(f"✓ 客户端回调已注册: {callback_type}") else: print(f"✗ 无效的回调类型: {callback_type}") except Exception as e: print(f"✗ 客户端回调注册失败: {e}") def unregister_client_callback(self, callback_type: str, callback: callable): """ 注销客户端回调 Args: callback_type: 回调类型 callback: 回调函数 """ try: if callback_type in self.client_callbacks: if callback in self.client_callbacks[callback_type]: self.client_callbacks[callback_type].remove(callback) print(f"✓ 客户端回调已注销: {callback_type}") else: print(f"✗ 无效的回调类型: {callback_type}") except Exception as e: print(f"✗ 客户端回调注销失败: {e}")