mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
尝试使用源生成器规范NodeModel代码逻辑
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<TResult>,将会自动等待异步完成并获取结果(无法处理Task<Task<TResult>>的情况)。</para>
|
||||
/// <para>如果返回了值类型,会自动装箱为引用对象。</para>
|
||||
/// <para>如果有方法执行过程中发送消息的需求,请在入参中声明以下类型的成员,调用时将传入发送消息的委托。</para>
|
||||
@@ -61,9 +62,24 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
/// <para>会进行异步等待,当Task结束后,自动获取TResult进行发送(请避免Task<Task<TResult>>诸如此类的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;
|
||||
|
||||
15
Library/Network/WebSocket/Handle/Attribute.cs
Normal file
15
Library/Network/WebSocket/Handle/Attribute.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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*/
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user