Files
whale-town/scripts/NetworkManager.gd
2025-12-05 19:00:14 +08:00

242 lines
7.1 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends Node
class_name NetworkManager
## 网络管理器
## 负责管理客户端与服务器的 WebSocket 连接
# 信号定义
signal connected_to_server()
signal disconnected_from_server()
signal connection_error(error: String)
signal message_received(message: Dictionary)
# 安全管理器
var security_manager: SecurityManager
var rate_limiter: RateLimiter
# WebSocket 客户端
var _client: WebSocketPeer = null
var _server_url: String = "ws://localhost:8080"
var _is_connected: bool = false
# 重连相关
var _reconnect_attempts: int = 0
var _max_reconnect_attempts: int = 5
var _reconnect_delay: float = 1.0
var _reconnect_timer: float = 0.0
var _should_reconnect: bool = false
# 连接超时相关
var _connection_timeout: float = 10.0
var _connection_timer: float = 0.0
var _is_connecting: bool = false
func _ready():
"""初始化网络管理器"""
# 使用默认网络设置避免GameConfig依赖
_max_reconnect_attempts = 3
_reconnect_delay = 1.0
_connection_timeout = 10.0
# 初始化安全组件
security_manager = SecurityManager.new()
add_child(security_manager)
rate_limiter = RateLimiter.new()
add_child(rate_limiter)
print("[NETWORK] NetworkManager initialized")
func connect_to_server(url: String = "") -> void:
"""连接到服务器"""
if not url.is_empty():
_server_url = url
print("[NETWORK] Connecting to server: ", _server_url)
# 如果已经在连接中,先断开
if _is_connecting:
disconnect_from_server()
_client = WebSocketPeer.new()
var err = _client.connect_to_url(_server_url)
if err != OK:
var error_msg = "连接失败: " + str(err)
print("[NETWORK] Failed to connect: ", err)
connection_error.emit(error_msg)
return
# 开始连接超时计时
_is_connecting = true
_connection_timer = _connection_timeout
print("[NETWORK] Connection initiated (timeout: ", _connection_timeout, "s)")
func disconnect_from_server() -> void:
"""断开服务器连接"""
if _client:
_client.close()
_client = null
_is_connected = false
_is_connecting = false
_connection_timer = 0.0
print("Disconnected from server")
disconnected_from_server.emit()
func send_message(message: Dictionary) -> void:
"""发送消息到服务器(使用消息协议)"""
if not _is_connected or not _client:
print("Cannot send message: not connected")
return
# 速率限制检查
var client_id = "local_client" # 本地客户端标识
if not rate_limiter.is_message_allowed(client_id):
print("[WARNING] [NETWORK] Message blocked by rate limiter")
return
# 安全验证消息格式
if not SecurityManager.validate_message_format(message):
print("[ERROR] [NETWORK] Invalid message format blocked: ", message.get("type", "unknown"))
return
# 使用 MessageProtocol 序列化
var json_string = MessageProtocol.serialize(message)
_client.send_text(json_string)
print("Sent message: ", message.get("type", "unknown"))
func send_typed_message(type: MessageProtocol.MessageType, data: Dictionary = {}) -> void:
"""发送指定类型的消息"""
var message = MessageProtocol.create_message(type, data)
send_message(message)
func is_server_connected() -> bool:
"""检查是否已连接到服务器"""
return _is_connected
func _process(delta):
"""处理网络消息和重连逻辑"""
# 处理重连计时器
if _should_reconnect and _reconnect_timer > 0:
_reconnect_timer -= delta
if _reconnect_timer <= 0:
_attempt_reconnect()
# 处理连接超时
if _is_connecting and _connection_timer > 0:
_connection_timer -= delta
if _connection_timer <= 0:
_handle_connection_timeout()
return
if not _client:
return
_client.poll()
var state = _client.get_ready_state()
# 检查连接状态
if state == WebSocketPeer.STATE_OPEN:
if not _is_connected:
_is_connected = true
_is_connecting = false # 连接成功,停止超时计时
_connection_timer = 0.0
_reconnect_attempts = 0 # 重置重连计数
_should_reconnect = false
print("Connected to server!")
connected_to_server.emit()
# 接收消息
while _client.get_available_packet_count() > 0:
var packet = _client.get_packet()
var json_string = packet.get_string_from_utf8()
# 检查消息长度防止DoS攻击
if json_string.length() > 10000: # 10KB限制
print("[ERROR] [NETWORK] Message too large, potential DoS attack. Size: ", json_string.length())
continue
# 使用 MessageProtocol 反序列化
var message = MessageProtocol.deserialize(json_string)
if not message.is_empty():
# 验证消息格式(基础验证)
if MessageProtocol.validate_message(message):
# 安全验证消息格式(增强验证)
if SecurityManager.validate_message_format(message):
print("Received message: ", message.get("type", "unknown"))
message_received.emit(message)
else:
print("[ERROR] [NETWORK] Security validation failed for message: ", message.get("type", "unknown"))
else:
print("[ERROR] [NETWORK] Invalid message format: ", json_string)
else:
print("[ERROR] [NETWORK] Failed to parse message: ", json_string)
elif state == WebSocketPeer.STATE_CLOSING:
pass
elif state == WebSocketPeer.STATE_CLOSED:
if _is_connected:
_is_connected = false
print("Connection closed")
disconnected_from_server.emit()
# 触发重连
_trigger_reconnect()
elif _is_connecting:
# 连接过程中关闭,可能是连接失败
_handle_connection_failure()
_client = null
func _trigger_reconnect() -> void:
"""触发重连逻辑"""
if _reconnect_attempts < _max_reconnect_attempts:
_should_reconnect = true
# 指数退避1秒、2秒、4秒
_reconnect_timer = _reconnect_delay * pow(2, _reconnect_attempts)
print("Will attempt reconnect in ", _reconnect_timer, " seconds (attempt ", _reconnect_attempts + 1, "/", _max_reconnect_attempts, ")")
else:
print("Max reconnect attempts reached")
connection_error.emit("Failed to reconnect after " + str(_max_reconnect_attempts) + " attempts")
func _attempt_reconnect() -> void:
"""尝试重新连接"""
_reconnect_attempts += 1
print("Attempting to reconnect... (attempt ", _reconnect_attempts, "/", _max_reconnect_attempts, ")")
connect_to_server()
func reset_reconnect() -> void:
"""重置重连状态"""
_reconnect_attempts = 0
_should_reconnect = false
_reconnect_timer = 0.0
func _handle_connection_timeout() -> void:
"""处理连接超时"""
print("[ERROR] [NETWORK] Connection timeout after ", _connection_timeout, " seconds")
_is_connecting = false
_connection_timer = 0.0
# 关闭WebSocketPeer连接
if _client:
_client.close()
_client = null
connection_error.emit("连接超时:无法连接到服务器,请检查服务器是否启动")
func _handle_connection_failure() -> void:
"""处理连接失败"""
print("[ERROR] [NETWORK] Connection failed during handshake")
_is_connecting = false
_connection_timer = 0.0
connection_error.emit("连接失败:服务器拒绝连接或服务器未启动")
func set_connection_timeout(timeout: float) -> void:
"""设置连接超时时间"""
_connection_timeout = timeout
func get_connection_timeout() -> float:
"""获取连接超时时间"""
return _connection_timeout