EG/plugins/user/server_architecture/clients/client_manager.py
2025-12-12 16:16:15 +08:00

847 lines
30 KiB
Python

"""
客户端管理器模块
负责管理客户端连接、认证和断开
"""
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}")