Files
5.24BuYu/Assets/MyGame/Scripts/Net/WebSocketManager.cs
T
2026-08-01 00:49:49 +08:00

343 lines
11 KiB
C#
Raw 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.
using Assets.MyGame.Scripts.Net.WebResponses;
using Best.WebSockets;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Assets.MyGame.Scripts.Net
{
public enum SocketType
{
login,
room,
game,
chess,
customer,
}
public partial class WebSocketManager : MonoSingleton<WebSocketManager>
{
private readonly Dictionary<SocketType, WebSocket> sockets = new();
private readonly Dictionary<SocketType, string> socketPaths = new();
private readonly Dictionary<SocketType, string> socketUrls = new();
private readonly Dictionary<SocketType, Action> handshakeActions = new();
private readonly HashSet<SocketType> handshakeCompleted = new();
private readonly HashSet<SocketType> reconnecting = new();
private readonly HashSet<SocketType> manualClosing = new();
private float nextHeartbeatTime;
private const float HeartbeatIntervalSeconds = 20f;
#if ENABLE_LOG_FILE
private const string baseUrl = "ws://47.109.132.12";
//private const string baseUrl = "ws://game5241.wzaxr.top";
#elif UNITY_EDITOR
//private const string baseUrl = "ws://8.137.163.153";
private const string baseUrl = "ws://47.109.132.12";
#else
//private const string baseUrl = "ws://47.109.132.12";
private const string baseUrl = "ws://47.109.34.50";
#endif
public bool isGame = false;
protected override void Awake()
{
base.Awake();
socketPaths[SocketType.game] = "/fishinghub";
socketPaths[SocketType.room] = "/fishingroomhub";
socketPaths[SocketType.login] = "/loginhub";
socketPaths[SocketType.chess] = "/chessgamehub";
socketPaths[SocketType.customer] = "/customerHub";
nextHeartbeatTime = Time.realtimeSinceStartup + HeartbeatIntervalSeconds;
}
private void Update()
{
if (Time.realtimeSinceStartup < nextHeartbeatTime)
return;
nextHeartbeatTime = Time.realtimeSinceStartup + HeartbeatIntervalSeconds;
foreach (var pair in sockets)
{
if (pair.Value != null && pair.Value.IsOpen && handshakeCompleted.Contains(pair.Key))
pair.Value.Send("{\"type\":6}\u001e");
}
}
/// <summary>
/// 创建或返回已存在的 WebSocket
/// </summary>
public WebSocket Connect(SocketType type, Action action = null)
{
return Connect(type, null, action);
}
/// <summary>
/// 创建或返回已存在的 WebSocket(可指定动态 URL,用于 /api/Room/Assign 返回的房间专属地址)
/// </summary>
public WebSocket Connect(SocketType type, string url, Action action = null)
{
// 已存在则直接返回
if (sockets.TryGetValue(type, out var exist))
{
if (exist.IsOpen)
{
#if UNITY_EDITOR
Debug.Log($"[WebSocket] {type} 已连接,直接返回");
#endif
switch (type)
{
case SocketType.login:
break;
case SocketType.room:
break;
case SocketType.game:
break;
case SocketType.chess:
UIManager.Instance.Get<ChineseChessMainPanel>(UIPanel.MainGamePanelZGXQ).subUIManager.ShowAsync(UIPanel.ZGXQDistrict);
break;
case SocketType.customer:
break;
default:
break;
}
if (action != null)
action?.Invoke();
return exist;
}
}
// 未指定 url 时用默认 baseUrl + socketPaths
if (string.IsNullOrEmpty(url))
url = baseUrl + socketPaths[type];
socketUrls[type] = url;
handshakeActions[type] = action;
var ws = new WebSocket(new Uri(url));
// Best.WebSockets 1.22.3 bug:服务器返回 3xx 重定向时,InternalRequest_OnBeforeRedirection
// 会再次调用 StartOpen(),但实例已 open 过,抛 "Open already called!"。
// 这里禁用内部 HTTP 请求的重定向跟随来规避。
ws.OnInternalRequestCreated += (socket, req) =>
{
req.RedirectSettings.MaxRedirects = 0;
};
sockets[type] = ws;
// 事件绑定
ws.OnOpen += (web) =>
{
#if UNITY_EDITOR
Debug.Log($"[WebSocket] {type} 已连接: {url}");
#endif
reconnecting.Remove(type);
handshakeCompleted.Remove(type);
// SignalR 握手完成前不能发送 Bind、JoinRoom 等 Hub 调用。
web.Send("{\"protocol\":\"json\",\"version\":1}\u001e");
};
ws.OnClosed += (web, code, reason) =>
{
#if UNITY_EDITOR
Debug.Log($"[WebSocket] {type} 连接关闭 Code:{code} Reason:{reason}");
#endif
handshakeCompleted.Remove(type);
if (sockets.TryGetValue(type, out var current) && ReferenceEquals(current, web))
sockets.Remove(type);
if (manualClosing.Remove(type))
return;
// 自动重连
switch (type)
{
case SocketType.login:
StartReconnect(type);
break;
case SocketType.room:
break;
case SocketType.game:
if (isGame)
{
int sitpos = 0;
foreach (var item in GameManager.Instance.gameData.currRoomData.players)
{
if (item.userId == GameManager.Instance.user.id)
{
sitpos = item.position; break;
}
}
StartReconnect(type, () =>
{
WebSocketSendList.JoinRoom(GameManager.Instance.gameData.currRoomData.roomId, GameManager.Instance.user.id, sitpos);
});
}
break;
case SocketType.chess:
break;
case SocketType.customer:
break;
default:
break;
}
};
ws.OnMessage += (web, msg) =>
{
OnMessage(web, msg, type);
};
// 保存
sockets[type] = ws;
// 发起连接
ws.Open();
return ws;
}
/// <summary>
/// 发送消息
/// </summary>
public void Send(SocketType type, string msg)
{
if (sockets.TryGetValue(type, out var ws) && ws.IsOpen)
{
ws.Send(msg + "\u001e");
#if UNITY_EDITOR
Debug.LogWarning($"[WebSocket] {type}Send :" + msg);
#endif
}
else
{
#if UNITY_EDITOR
Debug.LogWarning($"[WebSocket] {type} 未连接,无法发送消息" + msg);
UIManager.Instance.ShowBoardError($"[WebSocket] {type} 未连接,无法发送消息");
#endif
//switch (type)
//{
// case SocketType.login:
// break;
// case SocketType.room:
// break;
// case SocketType.game:
// //等待界面添加
// UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, GameManager.Instance.gameData.currGameType);
// //UIManager.Instance.ShowBoardGongGao("网络异常,请重新进入房间");
// break;
// case SocketType.chess:
// break;
// case SocketType.customer:
// break;
// default:
// break;
//}
}
}
/// <summary>
/// 关闭某个 socket
/// </summary>
public void Close(SocketType type)
{
if (sockets.TryGetValue(type, out var ws))
{
manualClosing.Add(type);
handshakeCompleted.Remove(type);
ws.Close(WebSocketStatusCodes.NormalClosure, "正常关闭"); // 或者 CloseStatusCode.Normal
//ws.Close();
sockets.Remove(type);
}
}
/// <summary>
/// 关闭所有 socket
/// </summary>
public void CloseAll()
{
foreach (var type in new List<SocketType>(sockets.Keys))
{
Close(type);
}
}
/// <summary>
/// 自动重连
/// </summary>
private void StartReconnect(SocketType type, Action action = null)
{
if (!reconnecting.Add(type))
return;
StartCoroutine(Reconnect(type, action));
}
private System.Collections.IEnumerator Reconnect(SocketType type, Action action = null)
{
#if UNITY_EDITOR
Debug.Log($"[WebSocket] {type} 3 秒后尝试重连...");
#endif
yield return new WaitForSeconds(3f);
if (manualClosing.Contains(type))
{
reconnecting.Remove(type);
yield break;
}
socketUrls.TryGetValue(type, out var url);
Connect(type, url, action);
}
private void OnHandshakeCompleted(SocketType type)
{
if (!handshakeCompleted.Add(type))
return;
switch (type)
{
case SocketType.login:
WebSocketSendList.Bind(GameManager.Instance.user.id);
break;
case SocketType.chess:
UIManager.Instance.ShowAsync(UIPanel.MainGamePanelZGXQ);
break;
}
if (handshakeActions.TryGetValue(type, out var action) && action != null)
{
handshakeActions[type] = null;
action.Invoke();
}
}
/// <summary>
/// 关闭所有 socket
/// </summary>
private void OnDestroy()
{
foreach (var type in new List<SocketType>(sockets.Keys))
manualClosing.Add(type);
foreach (var ws in new List<WebSocket>(sockets.Values))
{
ws.Close();
}
sockets.Clear();
}
}
}