This commit is contained in:
2026-08-04 21:20:56 +08:00
parent 3359483dfe
commit 8e475e6ab2
14 changed files with 823 additions and 171 deletions
@@ -3,6 +3,7 @@ using Best.WebSockets;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -15,53 +16,64 @@ namespace Assets.MyGame.Scripts.Net
{
public void OnMessage(WebSocket web, string msg, SocketType type)
{
#if UNITY_EDITOR
Debug.Log($"[WebSocket] {type} 收到消息: {msg}");
#endif
string[] msgs = msg.Split('\u001e');
foreach (var item in msgs)
NetLog.Log($"[WebSocket] {type} 收到消息: {msg}");
// ⭐ 优化:用 char 分割避免 Split 产生 string[] 数组 GC
// 单条消息通常只有 1-2 个分隔段,直接手动解析
int start = 0;
int sep;
while ((sep = msg.IndexOf('\u001e', start)) >= 0)
{
if (string.IsNullOrWhiteSpace(item))
continue;
ProcessSingleMessage(msg, start, sep - start, type);
start = sep + 1;
}
// 处理最后一段(或没有分隔符的整条消息)
if (start < msg.Length)
ProcessSingleMessage(msg, start, msg.Length - start, type);
}
if (item.Trim() == "{}")
{
OnHandshakeCompleted(type);
continue;
}
private void ProcessSingleMessage(string msg, int start, int length, SocketType type)
{
// 跳过空白段
if (length <= 0) return;
var v = JsonConvert.DeserializeObject<WebSocketResponseBase<object>>(item);
if (v == null)
{
continue;
}
// TODO: 这里可以做统一消息分发
#region 消息处理
switch (type)
{
case SocketType.login:
LoginMSG(item, v);
break;
case SocketType.room:
case SocketType.game:
FishMSG(item, v);
break;
case SocketType.chess:
ChineseChess(item, v);
break;
case SocketType.customer:
CustomerMSG(item, v);
break;
default:
break;
}
// 快速检查 "{}"(握手包),避免 Substring + Trim
if (length == 2 && msg[start] == '{' && msg[start + 1] == '}')
{
OnHandshakeCompleted(type);
return;
}
// 提取子串(此处必须分配,后续反序列化需要)
string item = msg.Substring(start, length);
// ⭐ 优化:用 JsonTextReader 只读 type/target 做路由,跳过 arguments 反序列化
// 原代码:JsonConvert.DeserializeObject<WebSocketResponseBase<object>>(item)
// 会反序列化整个 arguments 数组(包含鱼数据等),但这些数据在路由阶段完全不需要,
// 具体处理方法内部会重新反序列化。这能消除预解析阶段 90%+ 的 GC。
var v = ParseHeader(item);
if (v == null) return;
#region 消息处理
switch (type)
{
case SocketType.login:
LoginMSG(item, v);
break;
case SocketType.room:
case SocketType.game:
FishMSG(item, v);
break;
case SocketType.chess:
ChineseChess(item, v);
break;
case SocketType.customer:
CustomerMSG(item, v);
break;
default:
break;
}
#endregion
}
private static void ChineseChess(string msg, WebSocketResponseBase<object> v)
{
@@ -687,5 +699,50 @@ namespace Assets.MyGame.Scripts.Net
break;
}
}
/// <summary>
/// 轻量解析:用 JsonTextReader 只读 type 和 target 字段做路由,
/// 跳过 arguments 数组反序列化(节省 90%+ 预解析 GC)。
/// arguments 在具体处理方法内部按需反序列化。
/// </summary>
private static WebSocketResponseBase<object> ParseHeader(string item)
{
if (string.IsNullOrEmpty(item)) return null;
try
{
using (var sr = new StringReader(item))
using (var reader = new JsonTextReader(sr))
{
var result = new WebSocketResponseBase<object>();
while (reader.Read())
{
if (reader.TokenType == JsonToken.PropertyName)
{
string propName = (string)reader.Value;
// 读到 arguments 就停止,不再解析后续(节省大量 GC)
if (propName == "arguments")
break;
reader.Read(); // 读值
if (propName == "type")
{
result.type = Convert.ToInt32(reader.Value);
}
else if (propName == "target")
{
result.target = reader.Value as string;
}
// invocationId 等字段跳过
}
}
return result;
}
}
catch
{
return null;
}
}
}
}