724 lines
24 KiB
Python
724 lines
24 KiB
Python
"""
|
|
客户端管理器模块
|
|
管理客户端连接和状态
|
|
"""
|
|
|
|
import time
|
|
import threading
|
|
import hashlib
|
|
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_messages_per_second": 100,
|
|
"enable_banning": True,
|
|
"ban_duration": 3600,
|
|
"client_timeout": 60.0
|
|
}
|
|
|
|
# 客户端状态
|
|
self.client_state = {
|
|
"current_clients": 0,
|
|
"total_clients": 0,
|
|
"authenticated_clients": 0,
|
|
"banned_clients": 0
|
|
}
|
|
|
|
# 客户端存储
|
|
self.clients = {}
|
|
self.client_lock = threading.RLock()
|
|
|
|
# 客户端统计
|
|
self.client_stats = {
|
|
"clients_joined": 0,
|
|
"clients_left": 0,
|
|
"authentication_success": 0,
|
|
"authentication_failed": 0,
|
|
"messages_sent": 0,
|
|
"messages_received": 0,
|
|
"banned_clients": 0,
|
|
"errors": 0
|
|
}
|
|
|
|
# 黑名单管理
|
|
self.banned_ips = {}
|
|
self.banned_users = {}
|
|
|
|
# 回调函数
|
|
self.client_callbacks = {
|
|
"client_joined": [],
|
|
"client_left": [],
|
|
"client_authenticated": [],
|
|
"client_banned": [],
|
|
"client_error": []
|
|
}
|
|
|
|
# 时间戳记录
|
|
self.last_heartbeat_check = 0.0
|
|
self.last_cleanup = 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_heartbeat_check > self.client_config["heartbeat_interval"]:
|
|
self._check_client_heartbeats(current_time)
|
|
self.last_heartbeat_check = current_time
|
|
|
|
# 定期清理
|
|
if current_time - self.last_cleanup > 60.0: # 每分钟清理一次
|
|
self._cleanup_banned_clients(current_time)
|
|
self.last_cleanup = current_time
|
|
|
|
except Exception as e:
|
|
print(f"✗ 客户端管理器更新失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
def _check_client_heartbeats(self, current_time: float):
|
|
"""检查客户端心跳"""
|
|
try:
|
|
if not self.client_config["enable_heartbeat"]:
|
|
return
|
|
|
|
clients_to_disconnect = []
|
|
with self.client_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["client_timeout"]:
|
|
clients_to_disconnect.append(client_id)
|
|
|
|
for client_id in clients_to_disconnect:
|
|
self.disconnect_client(client_id, "timeout")
|
|
|
|
except Exception as e:
|
|
print(f"✗ 客户端心跳检查失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
|
|
def _cleanup_banned_clients(self, current_time: float):
|
|
"""清理过期的黑名单客户端"""
|
|
try:
|
|
expired_bans = []
|
|
for ip, ban_data in self.banned_ips.items():
|
|
if current_time - ban_data["ban_time"] > self.client_config["ban_duration"]:
|
|
expired_bans.append(ip)
|
|
|
|
for ip in expired_bans:
|
|
del self.banned_ips[ip]
|
|
self.client_state["banned_clients"] -= 1
|
|
|
|
except Exception as e:
|
|
print(f"✗ 黑名单清理失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
|
|
def _disconnect_all_clients(self):
|
|
"""断开所有客户端连接"""
|
|
try:
|
|
with self.client_lock:
|
|
client_ids = list(self.clients.keys())
|
|
|
|
for client_id in client_ids:
|
|
self.disconnect_client(client_id, "server_shutdown")
|
|
|
|
except Exception as e:
|
|
print(f"✗ 断开所有客户端连接失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
|
|
def add_client(self, client_id: str, client_info: Dict[str, Any]) -> bool:
|
|
"""
|
|
添加客户端
|
|
|
|
Args:
|
|
client_id: 客户端ID
|
|
client_info: 客户端信息
|
|
|
|
Returns:
|
|
是否添加成功
|
|
"""
|
|
try:
|
|
if not self.enabled:
|
|
return False
|
|
|
|
# 检查客户端数量限制
|
|
with self.client_lock:
|
|
if len(self.clients) >= self.client_config["max_clients"]:
|
|
print("✗ 客户端数量已达上限")
|
|
return False
|
|
|
|
# 检查IP是否被禁止
|
|
client_ip = client_info.get("ip_address", "")
|
|
if self._is_ip_banned(client_ip):
|
|
print(f"✗ IP地址被禁止: {client_ip}")
|
|
return False
|
|
|
|
# 添加客户端
|
|
client_data = {
|
|
"client_id": client_id,
|
|
"client_info": client_info,
|
|
"connect_time": time.time(),
|
|
"last_heartbeat": time.time(),
|
|
"is_authenticated": False,
|
|
"user_id": "",
|
|
"username": "",
|
|
"permissions": [],
|
|
"messages_sent": 0,
|
|
"messages_received": 0,
|
|
"bytes_sent": 0,
|
|
"bytes_received": 0
|
|
}
|
|
|
|
with self.client_lock:
|
|
self.clients[client_id] = client_data
|
|
|
|
self.client_state["current_clients"] = len(self.clients)
|
|
self.client_state["total_clients"] += 1
|
|
self.client_stats["clients_joined"] += 1
|
|
|
|
# 触发客户端加入回调
|
|
self._trigger_client_callback("client_joined", {
|
|
"client_id": client_id,
|
|
"client_info": client_info,
|
|
"timestamp": time.time()
|
|
})
|
|
|
|
print(f"✓ 客户端已添加: {client_id}")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"✗ 添加客户端失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
return False
|
|
|
|
def remove_client(self, client_id: str, reason: str = "unknown") -> bool:
|
|
"""
|
|
移除客户端
|
|
|
|
Args:
|
|
client_id: 客户端ID
|
|
reason: 移除原因
|
|
|
|
Returns:
|
|
是否移除成功
|
|
"""
|
|
try:
|
|
with self.client_lock:
|
|
if client_id in self.clients:
|
|
client_data = self.clients[client_id]
|
|
if client_data["is_authenticated"]:
|
|
self.client_state["authenticated_clients"] -= 1
|
|
del self.clients[client_id]
|
|
|
|
self.client_state["current_clients"] = len(self.clients)
|
|
self.client_stats["clients_left"] += 1
|
|
|
|
# 触发客户端离开回调
|
|
self._trigger_client_callback("client_left", {
|
|
"client_id": client_id,
|
|
"reason": reason,
|
|
"timestamp": time.time()
|
|
})
|
|
|
|
print(f"✓ 客户端已移除: {client_id} (原因: {reason})")
|
|
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 = "kicked") -> bool:
|
|
"""
|
|
断开客户端连接
|
|
|
|
Args:
|
|
client_id: 客户端ID
|
|
reason: 断开原因
|
|
|
|
Returns:
|
|
是否断开成功
|
|
"""
|
|
try:
|
|
# 通知WebSocket服务器断开连接
|
|
if self.plugin.websocket_server:
|
|
self.plugin.websocket_server.disconnect_client(client_id, reason)
|
|
|
|
# 移除客户端
|
|
return self.remove_client(client_id, reason)
|
|
|
|
except Exception as e:
|
|
print(f"✗ 断开客户端连接失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
return False
|
|
|
|
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 or not self.client_config["enable_authentication"]:
|
|
# 如果未启用认证,直接通过
|
|
with self.client_lock:
|
|
if client_id in self.clients:
|
|
self.clients[client_id]["is_authenticated"] = True
|
|
return True
|
|
|
|
# 使用认证管理器进行认证
|
|
if self.plugin.auth_manager:
|
|
auth_result = self.plugin.auth_manager.authenticate_client(client_id, auth_data)
|
|
if auth_result:
|
|
# 更新客户端认证状态
|
|
with self.client_lock:
|
|
if client_id in self.clients:
|
|
self.clients[client_id]["is_authenticated"] = True
|
|
self.clients[client_id]["user_id"] = auth_data.get("user_id", "")
|
|
self.clients[client_id]["username"] = auth_data.get("username", "")
|
|
self.clients[client_id]["permissions"] = auth_data.get("permissions", [])
|
|
|
|
self.client_state["authenticated_clients"] += 1
|
|
self.client_stats["authentication_success"] += 1
|
|
|
|
# 触发客户端认证回调
|
|
self._trigger_client_callback("client_authenticated", {
|
|
"client_id": client_id,
|
|
"user_id": auth_data.get("user_id", ""),
|
|
"username": auth_data.get("username", ""),
|
|
"timestamp": time.time()
|
|
})
|
|
|
|
print(f"✓ 客户端认证成功: {client_id}")
|
|
return True
|
|
else:
|
|
self.client_stats["authentication_failed"] += 1
|
|
print(f"✗ 客户端认证失败: {client_id}")
|
|
return False
|
|
else:
|
|
print("✗ 认证管理器不可用")
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ 客户端认证失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
return False
|
|
|
|
def update_client_heartbeat(self, client_id: str) -> bool:
|
|
"""
|
|
更新客户端心跳
|
|
|
|
Args:
|
|
client_id: 客户端ID
|
|
|
|
Returns:
|
|
是否更新成功
|
|
"""
|
|
try:
|
|
with self.client_lock:
|
|
if client_id in self.clients:
|
|
self.clients[client_id]["last_heartbeat"] = time.time()
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ 更新客户端心跳失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
return False
|
|
|
|
def get_client(self, client_id: str) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
获取客户端信息
|
|
|
|
Args:
|
|
client_id: 客户端ID
|
|
|
|
Returns:
|
|
客户端信息或None
|
|
"""
|
|
try:
|
|
with self.client_lock:
|
|
if client_id in self.clients:
|
|
return self.clients[client_id].copy()
|
|
else:
|
|
return None
|
|
|
|
except Exception as e:
|
|
print(f"✗ 获取客户端信息失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
return None
|
|
|
|
def get_all_clients(self) -> Dict[str, Any]:
|
|
"""
|
|
获取所有客户端信息
|
|
|
|
Returns:
|
|
所有客户端信息字典
|
|
"""
|
|
try:
|
|
with self.client_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 ban_client(self, client_id: str, reason: str = "violation", duration: float = 3600) -> bool:
|
|
"""
|
|
禁止客户端
|
|
|
|
Args:
|
|
client_id: 客户端ID
|
|
reason: 禁止原因
|
|
duration: 禁止时长(秒)
|
|
|
|
Returns:
|
|
是否禁止成功
|
|
"""
|
|
try:
|
|
if not self.client_config["enable_banning"]:
|
|
return False
|
|
|
|
client_data = self.get_client(client_id)
|
|
if not client_data:
|
|
return False
|
|
|
|
client_ip = client_data["client_info"].get("ip_address", "")
|
|
if client_ip:
|
|
# 添加到IP黑名单
|
|
self.banned_ips[client_ip] = {
|
|
"client_id": client_id,
|
|
"ban_time": time.time(),
|
|
"reason": reason,
|
|
"duration": duration
|
|
}
|
|
|
|
self.client_state["banned_clients"] += 1
|
|
self.client_stats["banned_clients"] += 1
|
|
|
|
# 断开客户端连接
|
|
self.disconnect_client(client_id, f"banned: {reason}")
|
|
|
|
# 触发客户端禁止回调
|
|
self._trigger_client_callback("client_banned", {
|
|
"client_id": client_id,
|
|
"ip_address": client_ip,
|
|
"reason": reason,
|
|
"duration": duration,
|
|
"timestamp": time.time()
|
|
})
|
|
|
|
print(f"✓ 客户端已被禁止: {client_id} (IP: {client_ip})")
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ 禁止客户端失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
return False
|
|
|
|
def _is_ip_banned(self, ip_address: str) -> bool:
|
|
"""
|
|
检查IP是否被禁止
|
|
|
|
Args:
|
|
ip_address: IP地址
|
|
|
|
Returns:
|
|
IP是否被禁止
|
|
"""
|
|
try:
|
|
if not self.client_config["enable_banning"]:
|
|
return False
|
|
|
|
if ip_address in self.banned_ips:
|
|
ban_data = self.banned_ips[ip_address]
|
|
if time.time() - ban_data["ban_time"] < ban_data["duration"]:
|
|
return True
|
|
else:
|
|
# 禁止时间已过期,移除
|
|
del self.banned_ips[ip_address]
|
|
self.client_state["banned_clients"] -= 1
|
|
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ IP禁止检查失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
return False
|
|
|
|
def unban_ip(self, ip_address: str) -> bool:
|
|
"""
|
|
解除IP禁止
|
|
|
|
Args:
|
|
ip_address: IP地址
|
|
|
|
Returns:
|
|
是否解除成功
|
|
"""
|
|
try:
|
|
if ip_address in self.banned_ips:
|
|
del self.banned_ips[ip_address]
|
|
self.client_state["banned_clients"] -= 1
|
|
print(f"✓ IP禁止已解除: {ip_address}")
|
|
return True
|
|
else:
|
|
print(f"✗ IP未被禁止: {ip_address}")
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ 解除IP禁止失败: {e}")
|
|
self.client_stats["errors"] += 1
|
|
return False
|
|
|
|
def is_client_authenticated(self, client_id: str) -> bool:
|
|
"""
|
|
检查客户端是否已认证
|
|
|
|
Args:
|
|
client_id: 客户端ID
|
|
|
|
Returns:
|
|
客户端是否已认证
|
|
"""
|
|
try:
|
|
with self.client_lock:
|
|
if client_id in self.clients:
|
|
return self.clients[client_id]["is_authenticated"]
|
|
else:
|
|
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 {
|
|
"state": self.client_state.copy(),
|
|
"stats": self.client_stats.copy(),
|
|
"config": self.client_config.copy()
|
|
}
|
|
|
|
def reset_stats(self):
|
|
"""重置客户端统计信息"""
|
|
try:
|
|
self.client_stats = {
|
|
"clients_joined": 0,
|
|
"clients_left": 0,
|
|
"authentication_success": 0,
|
|
"authentication_failed": 0,
|
|
"messages_sent": 0,
|
|
"messages_received": 0,
|
|
"banned_clients": 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}") |