尝试使用源生成器规范NodeModel代码逻辑

This commit is contained in:
fengjiayi
2024-10-20 12:10:57 +08:00
parent 9931fa7436
commit e38833a58c
127 changed files with 5173 additions and 1839 deletions

View File

@@ -1,22 +1,17 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serein.Library.Api;
using Serein.Library.Attributes;
using Serein.Library.Entity;
using Serein.Library.Utils;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Enum = System.Enum;
using Type = System.Type;

View File

@@ -1,7 +1,4 @@
using Serein.Library.Api;
using Serein.Library.Attributes;
using Serein.Library.Utils;
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;

View File

@@ -31,9 +31,10 @@ namespace Serein.Library.Network.WebSocketCommunication
/// <summary>
/// <para>作用WebSocket中处理Json时将通过Json中ThemeKey 对应的内容ThemeValue自动路由到相应方法进行处理。</para>
/// <para>如果没有显式设置ThemeValue将默认使用方法名称作为ThemeValue。</para>
/// <para>如果没有显式设置IsReturnValue标记为false当方法顺利完成没有抛出异常且返回对象非null会自动转为json文本发送回去</para>
/// <para>作用WebSocket中处理Json时将通过Json中ThemeKey 对应的内容ThemeValue自动路由到相应方法进行处理同时要求Data中必须存在对应入参。</para>
/// <para>如果没有显式设置 ThemeValue将默认使用方法名称作为ThemeValue。</para>
/// <para>如果没有显式设置 IsReturnValue 标记为 false 当方法顺利完成没有抛出异常且返回对象非null会自动转为json文本发送回去</para>
/// <para>如果没有显式设置 ArgNotNull 标记为 false ,当外部尝试调用时,若 Json Data 不包含响应的数据,将会被忽略此次调用</para>
/// <para>如果返回类型为Task或Task&lt;TResult&gt;将会自动等待异步完成并获取结果无法处理Task&lt;Task&lt;TResult&gt;&gt;的情况)。</para>
/// <para>如果返回了值类型,会自动装箱为引用对象。</para>
/// <para>如果有方法执行过程中发送消息的需求,请在入参中声明以下类型的成员,调用时将传入发送消息的委托。</para>
@@ -61,9 +62,24 @@ namespace Serein.Library.Network.WebSocketCommunication
/// <para>会进行异步等待当Task结束后自动获取TResult进行发送请避免Task&lt;Task&lt;TResult&gt;&gt;诸如此类的Task泛型嵌套</para>
/// </summary>
public bool IsReturnValue = true;
/// <summary>
/// <para>表示该方法所有入参不能为空所需的参数在请求Json的Data不存在</para>
/// <para>若有一个参数无法从data获取则不会进行调用该方法</para>
/// <para>如果设置该属性为 false ,但某些入参不能为空,而不希望在代码中进行检查,请为入参添加[NotNull]/[Needful]特性</para>
/// </summary>
public bool ArgNotNull = true;
}
internal class SocketHandleModel
/// <summary>
/// 使用消息DataKey整体数据
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class UseMsgDataAttribute : Attribute
{
}
internal class SocketHandleModule
{
public string ThemeValue { get; set; } = string.Empty;
public bool IsReturnValue { get; set; } = true;

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication.Handle
{
/// <summary>
/// 表示参数可以为空(Net462不能使用NutNull的情况
/// </summary>
public sealed class NeedfulAttribute : Attribute
{
}
}

View File

@@ -4,7 +4,9 @@ using Serein.Library.Utils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@@ -20,53 +22,164 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
/// </summary>
public class JsonMsgHandleConfig
{
private readonly Delegate EmitDelegate;
private readonly EmitHelper.EmitMethodType EmitMethodType;
public Guid HandleGuid { get; }
private Action<Exception, Action<object>> OnExceptionTracking;
internal JsonMsgHandleConfig(SocketHandleModel model,ISocketHandleModule instance, MethodInfo methodInfo, Action<Exception, Action<object>> onExceptionTracking)
internal JsonMsgHandleConfig(SocketHandleModule model,
ISocketHandleModule instance,
MethodInfo methodInfo,
Action<Exception, Action<object>> onExceptionTracking,
bool ArgNotNull)
{
EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo,out EmitDelegate);
this.Model = model;
this.Module = model;
Instance = instance;
var parameterInfos = methodInfo.GetParameters();
ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray();
ParameterName = parameterInfos.Select(t => t.Name).ToArray();
this.ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray();
this.ParameterName = parameterInfos.Select(t => t.Name).ToArray();
this.HandleGuid = instance.HandleGuid;
this.OnExceptionTracking = onExceptionTracking;
this.ArgNotNull = ArgNotNull;
this.useMsgData = parameterInfos.Select(p => p.GetCustomAttribute<UseMsgDataAttribute>() != null).ToArray();
#if NET5_0_OR_GREATER
this.IsCheckArgNotNull = parameterInfos.Select(p => p.GetCustomAttribute<NotNullAttribute>() != null).ToArray();
#endif
if(IsCheckArgNotNull is null)
{
IsCheckArgNotNull = parameterInfos.Select(p => p.GetCustomAttribute<NeedfulAttribute>() != null).ToArray();
}
else
{
// 兼容两种非空特性的写法
var argNotNull = parameterInfos.Select(p => p.GetCustomAttribute<NeedfulAttribute>() != null).ToArray();
for (int i = 0; i < IsCheckArgNotNull.Length; i++)
{
if (!IsCheckArgNotNull[i] && argNotNull[i])
{
IsCheckArgNotNull[i] = true;
}
}
}
}
private SocketHandleModel Model;
private ISocketHandleModule Instance;
public Guid HandleGuid { get; }
private string[] ParameterName;
private Type[] ParameterType;
/// <summary>
/// 参数不能为空
/// </summary>
private bool ArgNotNull;
/// <summary>
/// Emit委托
/// </summary>
private readonly Delegate EmitDelegate;
/// <summary>
/// Emit委托类型
/// </summary>
private readonly EmitHelper.EmitMethodType EmitMethodType;
/// <summary>
/// 未捕获的异常跟踪
/// </summary>
private readonly Action<Exception, Action<object>> OnExceptionTracking;
/// <summary>
/// 所在的模块
/// </summary>
private readonly SocketHandleModule Module;
/// <summary>
/// 所使用的实例
/// </summary>
private readonly ISocketHandleModule Instance;
/// <summary>
/// 参数名称
/// </summary>
private readonly string[] ParameterName;
/// <summary>
/// 参数类型
/// </summary>
private readonly Type[] ParameterType;
/// <summary>
/// 是否使用整体data参数
/// </summary>
private readonly bool[] useMsgData;
/// <summary>
/// 是否检查变量为空
/// </summary>
private readonly bool[] IsCheckArgNotNull;
public async void Handle(Func<string, Task> RecoverAsync, JObject jsonObject)
public async void Handle(Func<object, Task> SendAsync, JObject jsonObject)
{
object[] args = new object[ParameterType.Length];
bool isCanInvoke = true;; // 表示是否可以调用方法
for (int i = 0; i < ParameterType.Length; i++)
{
var type = ParameterType[i];
var argName = ParameterName[i];
if (type.IsGenericType)
if (useMsgData[i])
{
if (type.IsAssignableFrom(typeof(Func<object, Task>)))
args[i] = jsonObject.ToObject(type);
}
else if (type.IsValueType)
{
var jsonValue = jsonObject.GetValue(argName);
if (!(jsonValue is null))
{
args[i] = jsonValue.ToObject(type);
}
else
{
if (ArgNotNull && !IsCheckArgNotNull[i]) // 检查不能为空
{
args[i] = Activator.CreateInstance(type); // 值类型返回默认值
}
else
{
isCanInvoke = false; // 参数不能为空,终止调用
break;
}
}
}
else if (type.IsClass)
{
var jsonValue = jsonObject.GetValue(argName);
if (!(jsonValue is null))
{
args[i] = jsonValue.ToObject(type);
}
else
{
if (ArgNotNull && !IsCheckArgNotNull[i])
{
args[i] = null; // 引用类型返回null
}
else
{
isCanInvoke = false; // 参数不能为空,终止调用
break;
}
}
}
else if (type.IsGenericType) // 传递SendAsync委托
{
if (type.IsAssignableFrom(typeof(Func<object, Task>)))
{
args[i] = new Func<object, Task>(async data =>
{
var jsonText = JsonConvert.SerializeObject(data);
await RecoverAsync.Invoke(jsonText);
await SendAsync.Invoke(jsonText);
});
}
else if (type.IsAssignableFrom(typeof(Func<string, Task>)))
{
args[i] = new Func<string, Task>(async data =>
{
await RecoverAsync.Invoke(data);
await SendAsync.Invoke(data);
});
}
else if (type.IsAssignableFrom(typeof(Action<object>)))
@@ -74,7 +187,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
args[i] = new Action<object>(async data =>
{
var jsonText = JsonConvert.SerializeObject(data);
await RecoverAsync.Invoke(jsonText);
await SendAsync.Invoke(jsonText);
});
}
else if (type.IsAssignableFrom(typeof(Action<string>)))
@@ -82,28 +195,17 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
args[i] = new Action<string>(async data =>
{
var jsonText = JsonConvert.SerializeObject(data);
await RecoverAsync.Invoke(jsonText);
await SendAsync.Invoke(jsonText);
});
}
}
else if (type.IsValueType || type.IsClass)
{
var jsonValue = jsonObject.GetValue(argName);
if (jsonValue is null)
{
// 值类型返回默认值引用类型返回null
args[i] = type.IsValueType ? Activator.CreateInstance(type) : null;
}
else
{
args[i] = jsonValue.ToObject(type);
}
}
}
}
//Stopwatch sw = new Stopwatch();
//sw.Start();
if (!isCanInvoke)
{
return;
}
object result;
try
{
@@ -133,27 +235,21 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
{
var jsonText = JsonConvert.SerializeObject(data);
await RecoverAsync.Invoke(jsonText);
await SendAsync.Invoke(jsonText);
}));
}
//sw.Stop();
//Console.WriteLine($"Emit Invoke{sw.ElapsedTicks * 1000000F / Stopwatch.Frequency:n3}μs");
if(Model.IsReturnValue && result != null && result.GetType().IsClass)
if(Module.IsReturnValue && result != null && result.GetType().IsClass)
{
var reusltJsonText = JsonConvert.SerializeObject(result);
_ = RecoverAsync.Invoke($"{reusltJsonText}");
//var reusltJsonText = JsonConvert.SerializeObject(result);
_ = SendAsync.Invoke(result);
//_ = SendAsync.Invoke($"{reusltJsonText}");
}
}
public void Clear()
{
Instance = null;
ParameterName = null;
ParameterType = null;
//expressionDelegate = null;
}
}

View File

@@ -1,9 +1,12 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication.Handle
@@ -37,13 +40,21 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
/// </summary>
public ConcurrentDictionary<string, JsonMsgHandleConfig> MyHandleConfigs = new ConcurrentDictionary<string, JsonMsgHandleConfig>();
internal void AddHandleConfigs(SocketHandleModel model, ISocketHandleModule instance, MethodInfo methodInfo
, Action<Exception, Action<object>> onExceptionTracking)
/// <summary>
/// 添加处理配置
/// </summary>
/// <param name="module">处理模块</param>
/// <param name="jsonMsgHandleConfig">处理配置</param>
internal bool AddHandleConfigs(SocketHandleModule module,JsonMsgHandleConfig jsonMsgHandleConfig)
{
if (!MyHandleConfigs.ContainsKey(model.ThemeValue))
if (!MyHandleConfigs.ContainsKey(module.ThemeValue))
{
var jsonMsgHandleConfig = new JsonMsgHandleConfig(model,instance, methodInfo, onExceptionTracking);
MyHandleConfigs[model.ThemeValue] = jsonMsgHandleConfig;
MyHandleConfigs[module.ThemeValue] = jsonMsgHandleConfig;
return true;
}
else
{
return false;
}
}
@@ -72,18 +83,14 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
{
var temp = MyHandleConfigs.Values;
MyHandleConfigs.Clear();
foreach (var config in temp)
{
config.Clear();
}
}
/// <summary>
/// 处理JSON数据
/// </summary>
/// <param name="RecoverAsync"></param>
/// <param name="tSendAsync"></param>
/// <param name="jsonObject"></param>
public void HandleSocketMsg(Func<string, Task> RecoverAsync, JObject jsonObject)
public void HandleSocketMsg(Func<string, Task> tSendAsync, JObject jsonObject)
{
// 获取到消息
string themeKeyName = jsonObject.GetValue(ThemeJsonKey)?.ToString();
@@ -92,11 +99,36 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
// 没有主题
return;
}
if (jsonObject[DataJsonKey] is JObject dataJsonObject)
Func<object, Task> SendAsync = async (data) =>
{
handldConfig.Handle(RecoverAsync, dataJsonObject);
var sendMsg = new
{
theme = themeKeyName,
token = "",
data = data,
};
var msg = JsonConvert.SerializeObject(sendMsg);
await tSendAsync(msg);
};
try
{
JObject dataObj = jsonObject.GetValue(DataJsonKey).ToObject<JObject>();
handldConfig.Handle(SendAsync, dataObj);
}
catch (Exception ex)
{
Console.WriteLine($"error in ws : {ex.Message}{Environment.NewLine}json value:{jsonObject}");
return;
}
}
}
}

View File

@@ -93,7 +93,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
var themeKey = moduleAttribute.ThemeKey;
var dataKey = moduleAttribute.DataKey;
var handlemodule = AddMyHandleModule(themeKey, dataKey);
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Select(method =>
@@ -101,7 +101,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
var methodsAttribute = method.GetCustomAttribute<AutoSocketHandleAttribute>();
if (methodsAttribute is null)
{
return (null, null);
return (null, null,false);
}
else
{
@@ -109,13 +109,14 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
{
methodsAttribute.ThemeValue = method.Name;
}
var model = new SocketHandleModel
var model = new SocketHandleModule
{
IsReturnValue = methodsAttribute.IsReturnValue,
ThemeValue = methodsAttribute.ThemeValue,
};
var value = methodsAttribute.ThemeValue;
return (model, method);
var argNotNull = methodsAttribute.ArgNotNull;
return (model, method, argNotNull);
}
})
.Where(x => !(x.model is null)).ToList();
@@ -124,14 +125,21 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
return;
}
Console.WriteLine($"add websocket handle model :");
Console.WriteLine($"theme key, data key : {themeKey}, {dataKey}");
foreach ((var model, var method) in methods)
foreach ((var module, var method,var argNotNull) in methods)
{
Console.WriteLine($"theme value : {model.ThemeValue}");
Console.WriteLine($"theme value : {module.ThemeValue}");
try
{
handlemodule.AddHandleConfigs(model, socketControlBase, method, onExceptionTracking);
var jsonMsgHandleConfig = new JsonMsgHandleConfig(module, socketControlBase, method, onExceptionTracking, argNotNull);
var result = handlemodule.AddHandleConfigs(module,jsonMsgHandleConfig);
if (!result)
{
throw new Exception("添加失败,已经添加过相同的配置");
}
}
catch (Exception ex)
{
@@ -145,17 +153,17 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
/// <summary>
/// 异步处理消息
/// </summary>
/// <param name="RecoverAsync"></param>
/// <param name="SendAsync"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task HandleMsgAsync(Func<string, Task> RecoverAsync, string message)
public async Task HandleMsgAsync(Func<string, Task> SendAsync, string message)
{
JObject json = JObject.Parse(message);
await Task.Run(() =>
{
foreach (var module in MyHandleModuleDict.Values)
{
module.HandleSocketMsg(RecoverAsync, json);
module.HandleSocketMsg(SendAsync, json);
}
});

View File

@@ -1,10 +1,4 @@
using Serein.Library.Attributes;
using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System;
namespace Serein.Library.Network.WebSocketCommunication
{

View File

@@ -1,10 +1,5 @@
using Newtonsoft.Json.Linq;
using Serein.Library.Attributes;
using Serein.Library.Network.WebSocketCommunication.Handle;
using Serein.Library.Web;
using Serein.Library.Network.WebSocketCommunication.Handle;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.WebSockets;
using System.Text;
@@ -17,7 +12,6 @@ namespace Serein.Library.Network.WebSocketCommunication
/// <summary>
/// WebSocket客户端
/// </summary>
[AutoRegister]
public class WebSocketClient
{
/// <summary>
@@ -25,7 +19,7 @@ namespace Serein.Library.Network.WebSocketCommunication
/// </summary>
public WebSocketClient()
{
}
/// <summary>
@@ -40,10 +34,20 @@ namespace Serein.Library.Network.WebSocketCommunication
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
public async Task ConnectAsync(string uri)
public async Task<bool> ConnectAsync(string uri)
{
await _client.ConnectAsync(new Uri(uri), CancellationToken.None);
await ReceiveAsync();
try
{
await _client.ConnectAsync(new Uri(uri), CancellationToken.None);
_ = ReceiveAsync();
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
@@ -65,91 +69,106 @@ namespace Serein.Library.Network.WebSocketCommunication
private async Task ReceiveAsync()
{
var buffer = new byte[1024];
var receivedMessage = new StringBuilder(); // 用于拼接长消息
while (_client.State == WebSocketState.Open)
{
try
{
var result = await _client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
WebSocketReceiveResult result;
do
{
result = await _client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
// 根据接收到的字节数解码为部分字符串,并添加到 StringBuilder 中
var partialMessage = Encoding.UTF8.GetString(buffer, 0, result.Count);
receivedMessage.Append(partialMessage);
} while (!result.EndOfMessage); // 判断是否已经收到完整消息
// 处理收到的完整消息
if (result.MessageType == WebSocketMessageType.Close)
{
await _client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}
else
{
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息
Debug.WriteLine($"Received: {message}");
var completeMessage = receivedMessage.ToString();
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, completeMessage); // 处理消息
Debug.WriteLine($"Received: {completeMessage}");
}
// 清空 StringBuilder 为下一条消息做准备
receivedMessage.Clear();
}
catch (Exception ex)
{
Debug.WriteLine($"Received: {EX.ToString()}");
Debug.WriteLine($"Received: {ex.ToString()}");
}
}
}
}
/* #region 消息处理
private readonly string ThemeField;
private readonly ConcurrentDictionary<string, HandldConfig> ThemeConfigs = new ConcurrentDictionary<string, HandldConfig>();
/* #region 消息处理
private readonly string ThemeField;
private readonly ConcurrentDictionary<string, HandldConfig> ThemeConfigs = new ConcurrentDictionary<string, HandldConfig>();
public async Task HandleSocketMsg(string jsonStr)
{
JObject json;
try
public async Task HandleSocketMsg(string jsonStr)
{
json = JObject.Parse(jsonStr);
}
catch (Exception ex)
{
await SendAsync(_client, ex.Message);
return;
}
// 获取到消息
string themeName = json[ThemeField]?.ToString();
if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
return;
}
object dataValue;
if (string.IsNullOrEmpty(handldConfig.DataField))
{
dataValue = json.ToObject(handldConfig.DataType);
}
else
{
dataValue = json[handldConfig.DataField].ToObject(handldConfig.DataType);
}
await handldConfig.Invoke(dataValue, SendAsync);
}
public void AddConfig(string themeName, Type dataType, MsgHandler msgHandler)
{
if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
handldConfig = new HandldConfig
JObject json;
try
{
DataField = themeName,
DataType = dataType
};
ThemeConfigs.TryAdd(themeName, handldConfig);
}
handldConfig.HandldAsync += msgHandler;
}
public void RemoteConfig(string themeName, MsgHandler msgHandler)
{
if (ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
handldConfig.HandldAsync -= msgHandler;
if (!handldConfig.HasSubscribers)
json = JObject.Parse(jsonStr);
}
catch (Exception ex)
{
ThemeConfigs.TryRemove(themeName, out _);
await SendAsync(_client, ex.Message);
return;
}
// 获取到消息
string themeName = json[ThemeField]?.ToString();
if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
return;
}
object dataValue;
if (string.IsNullOrEmpty(handldConfig.DataField))
{
dataValue = json.ToObject(handldConfig.DataType);
}
else
{
dataValue = json[handldConfig.DataField].ToObject(handldConfig.DataType);
}
await handldConfig.Invoke(dataValue, SendAsync);
}
public void AddConfig(string themeName, Type dataType, MsgHandler msgHandler)
{
if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
handldConfig = new HandldConfig
{
DataField = themeName,
DataType = dataType
};
ThemeConfigs.TryAdd(themeName, handldConfig);
}
handldConfig.HandldAsync += msgHandler;
}
public void RemoteConfig(string themeName, MsgHandler msgHandler)
{
if (ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
handldConfig.HandldAsync -= msgHandler;
if (!handldConfig.HasSubscribers)
{
ThemeConfigs.TryRemove(themeName, out _);
}
}
}
#endregion*/
}
#endregion*/
}

View File

@@ -1,14 +1,10 @@
using Newtonsoft.Json.Linq;
using Serein.Library.Attributes;
using Serein.Library.Network.WebSocketCommunication.Handle;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Net;
using System.Net.WebSockets;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -35,15 +31,6 @@ namespace Serein.Library.Network.WebSocketCommunication
/// </summary>
public string AddresPort { get; }
/// <summary>
/// 是否已经鉴权
/// </summary>
public bool IsAuthorized { get => isAuthorized; } //set => isAuthorized = value;
/// <summary>
/// 是否已经鉴权
/// </summary>
private bool isAuthorized;
/// <summary>
/// 授权字段
@@ -61,34 +48,21 @@ namespace Serein.Library.Network.WebSocketCommunication
/// 处理消息授权
/// </summary>
/// <param name="message"></param>
public async Task HandleAuthorized(string message)
public async Task<bool> HandleAuthorized(string message)
{
if(!isAuthorized && semaphoreSlim is null) // 需要重新授权
{
semaphoreSlim = new SemaphoreSlim(1);
}
await semaphoreSlim.WaitAsync(1);
if(isAuthorized) // 授权通过,无须再次检查授权
{
return;
}
bool isAuthorized = false;
JObject json = JObject.Parse(message);
if(json.TryGetValue(TokenKey,out var token))
{
// 交给之前定义的授权方法进行判断
isAuthorized = await InspectionAuthorizedFunc?.Invoke(token);
if (isAuthorized)
{
// 授权通过,释放资源
semaphoreSlim.Release();
semaphoreSlim.Dispose();
semaphoreSlim = null;
}
}
else
{
isAuthorized = false;
}
return isAuthorized;
}
}
@@ -114,7 +88,7 @@ namespace Serein.Library.Network.WebSocketCommunication
{
this.AuthorizedClients = new ConcurrentDictionary<string, WebSocketAuthorizedHelper>();
this.InspectionAuthorizedFunc = (tokenObj) => Task.FromResult(true);
this.IsNeedInspectionAuthorized = false;
this.IsCheckToken = false;
}
/// <summary>
@@ -127,7 +101,7 @@ namespace Serein.Library.Network.WebSocketCommunication
this.TokenKey = tokenKey;
this.AuthorizedClients = new ConcurrentDictionary<string, WebSocketAuthorizedHelper>();
this.InspectionAuthorizedFunc = inspectionAuthorizedFunc;
this.IsNeedInspectionAuthorized = true;
this.IsCheckToken = true;
}
/// <summary>
@@ -136,7 +110,7 @@ namespace Serein.Library.Network.WebSocketCommunication
public ConcurrentDictionary<string, WebSocketAuthorizedHelper> AuthorizedClients;
private readonly string TokenKey;
private readonly Func<dynamic, Task<bool>> InspectionAuthorizedFunc;
private bool IsNeedInspectionAuthorized = false;
private bool IsCheckToken = false;
/// <summary>
/// 进行监听服务
/// </summary>
@@ -169,7 +143,7 @@ namespace Serein.Library.Network.WebSocketCommunication
if (context.Request.IsWebSocketRequest)
{
WebSocketAuthorizedHelper authorizedHelper = null;
if (IsNeedInspectionAuthorized)
if (IsCheckToken)
{
if (AuthorizedClients.TryAdd(clientPoint, new WebSocketAuthorizedHelper(clientPoint, TokenKey, InspectionAuthorizedFunc)))
{
@@ -200,69 +174,78 @@ namespace Serein.Library.Network.WebSocketCommunication
private async Task HandleWebSocketAsync(WebSocket webSocket, WebSocketAuthorizedHelper authorizedHelper)
{
// 需要授权,却没有成功创建授权类,关闭连接
if (IsNeedInspectionAuthorized && authorizedHelper is null)
if (IsCheckToken && authorizedHelper is null)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
return;
}
Func<string, Task> SendAsync = async (text) =>
{
await WebSocketServer.SendAsync(webSocket, text);
};
var buffer = new byte[1024];
var receivedMessage = new StringBuilder(); // 用于拼接长消息
while (webSocket.State == WebSocketState.Open)
{
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
WebSocketReceiveResult result;
try
{
SendAsync = null;
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
if (IsNeedInspectionAuthorized)
do
{
AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _);
}
}
else
{
var message = Encoding.UTF8.GetString(buffer, 0, result.Count); // 序列为文本
if(!IsNeedInspectionAuthorized)
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
// 将接收到的部分消息解码并拼接
var partialMessage = Encoding.UTF8.GetString(buffer, 0, result.Count);
receivedMessage.Append(partialMessage);
} while (!result.EndOfMessage); // 循环直到接收到完整的消息
// 完整消息已经接收到,准备处理
var message = receivedMessage.ToString();
if (result.MessageType == WebSocketMessageType.Close)
{
// 无须授权
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息
SendAsync = null;
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
if (IsCheckToken)
{
AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _);
}
}
else
{
// 需要授权
if (!authorizedHelper.IsAuthorized)
if (IsCheckToken)
{
// 该连接尚未验证授权,尝试检测授权
_ = SendAsync("正在授权");
await authorizedHelper.HandleAuthorized(message);
}
if (authorizedHelper.IsAuthorized)
{
// 该连接通过了验证
_ = SendAsync("授权成功");
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息
}
else
{
_ = SendAsync("授权失败");
var authorizedResult = await authorizedHelper.HandleAuthorized(message); // 尝试检测授权
if (!authorizedResult) // 授权失败
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
if (IsCheckToken)
{
AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _);
}
continue;
}
}
// 消息处理
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息
}
// 清空 StringBuilder 为下一条消息做准备
receivedMessage.Clear();
}
catch (Exception ex)
{
// 处理异常
Debug.WriteLine($"Error: {ex.ToString()}");
}
}
}
/// <summary>
/// 发送消息
/// </summary>