使用emit代替表达式树构造委托。

内置了websocket server与相应的导航功能,可在实例工程中找到相应的实现。
This commit is contained in:
fengjiayi
2024-10-10 10:45:53 +08:00
parent 0bab770f0a
commit d1b9a3f28f
43 changed files with 1953 additions and 392 deletions

View File

@@ -14,10 +14,10 @@ namespace Serein.Library.Web
/// HTTP接口监听类
/// </summary>
[AutoRegister]
public class WebServer
public class WebApiServer
{
private readonly IRouter Router;// 路由器
public WebServer(IRouter router)
public WebApiServer(IRouter router)
{
this.Router = router;
listener = new HttpListener();
@@ -33,7 +33,7 @@ namespace Serein.Library.Web
// 启动服务器
public WebServer Start(string prefixe)
public WebApiServer Start(string prefixe)
{
if (!prefixe.Substring(prefixe.Length - 1, 1).Equals(@"/"))

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net.WebSockets;
namespace Serein.Library.Network.WebSocketCommunication
{
[AttributeUsage(AttributeTargets.Method)]
public sealed class AutoSocketHandleAttribute : Attribute
{
public string ThemeValue;
//public Type DataType;
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class AutoSocketModuleAttribute : Attribute
{
public string JsonDataField;
public string JsonThemeField;
}
}

View File

@@ -0,0 +1,141 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serein.Library.Utils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication.Handle
{
public class MyHandleConfig
{
private readonly Delegate EmitDelegate;
private readonly EmitHelper.EmitMethodType EmitMethodType;
public MyHandleConfig(ISocketControlBase instance, MethodInfo methodInfo)
{
EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo,out EmitDelegate);
Instance = instance;
var parameterInfos = methodInfo.GetParameters();
ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray();
ParameterName = parameterInfos.Select(t => t.Name).ToArray();
}
public ISocketControlBase Instance { get; private set; }
private string[] ParameterName;
private Type[] ParameterType;
public async void Handle(Func<string, Task> RecoverAsync, JObject jsonObject)
{
object[] args = new object[ParameterType.Length];
for (int i = 0; i < ParameterType.Length; i++)
{
var type = ParameterType[i];
var argName = ParameterName[i];
if (type.IsGenericType)
{
if (type.IsAssignableFrom(typeof(Func<object, Task>)))
{
args[i] = new Func<object, Task>(async data =>
{
var jsonText = JsonConvert.SerializeObject(data);
await RecoverAsync.Invoke(jsonText);
});
}
else if (type.IsAssignableFrom(typeof(Func<string, Task>)))
{
args[i] = new Func<string, Task>(async data =>
{
await RecoverAsync.Invoke(data);
});
}
else if (type.IsAssignableFrom(typeof(Action<object>)))
{
args[i] = new Action<object>(async data =>
{
var jsonText = JsonConvert.SerializeObject(data);
await RecoverAsync.Invoke(jsonText);
});
}
else if (type.IsAssignableFrom(typeof(Action<string>)))
{
args[i] = new Action<string>(async data =>
{
var jsonText = JsonConvert.SerializeObject(data);
await RecoverAsync.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();
object result;
if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func<object, object[], Task<object>> hasResultTask)
{
result = await hasResultTask(Instance, args);
}
else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func<object, object[], Task> task)
{
await task.Invoke(Instance, args);
result = null;
}
else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func<object, object[], object> func)
{
result = func.Invoke(Instance, args);
}
else
{
throw new NotImplementedException("构造委托无法正确调用");
}
sw.Stop();
Console.WriteLine($"Emit Invoke{sw.ElapsedTicks * 1000000F / Stopwatch.Frequency:n3}μs");
if(result != null && result.GetType().IsClass)
{
var reusltJsonText = JsonConvert.SerializeObject(result);
_ = RecoverAsync.Invoke($"{reusltJsonText}");
}
}
public void Clear()
{
Instance = null;
ParameterName = null;
ParameterType = null;
//expressionDelegate = null;
}
}
}

View File

@@ -0,0 +1,69 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication.Handle
{
public class MyHandleModule
{
public MyHandleModule(string ThemeJsonKey, string DataJsonKey)
{
this.ThemeJsonKey = ThemeJsonKey;
this.DataJsonKey = DataJsonKey;
}
public string ThemeJsonKey { get; }
public string DataJsonKey { get; }
public ConcurrentDictionary<string, MyHandleConfig> MyHandleConfigs = new ConcurrentDictionary<string, MyHandleConfig>();
public void AddHandleConfigs(string themeValue, ISocketControlBase instance, MethodInfo methodInfo)
{
if (!MyHandleConfigs.ContainsKey(themeValue))
{
var myHandleConfig = new MyHandleConfig(instance, methodInfo);
MyHandleConfigs[themeValue] = myHandleConfig;
}
}
public void ResetConfig(ISocketControlBase socketControlBase)
{
foreach (var kv in MyHandleConfigs.ToArray())
{
var config = kv.Value;
if (config.Instance.HandleGuid.Equals(socketControlBase.HandleGuid))
{
MyHandleConfigs.TryRemove(kv.Key, out _);
}
}
}
public void ResetConfig()
{
var temp = MyHandleConfigs.Values;
MyHandleConfigs.Clear();
foreach (var config in temp)
{
config.Clear();
}
}
public void HandleSocketMsg(Func<string, Task> RecoverAsync, JObject jsonObject)
{
// 获取到消息
string themeKeyName = jsonObject.GetValue(ThemeJsonKey)?.ToString();
if (!MyHandleConfigs.TryGetValue(themeKeyName, out var handldConfig))
{
// 没有主题
return;
}
if (jsonObject[DataJsonKey] is JObject dataJsonObject)
{
handldConfig.Handle(RecoverAsync, dataJsonObject);
}
}
}
}

View File

@@ -0,0 +1,118 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
using Serein.Library.Utils;
using System.Net.Http.Headers;
using System.Linq.Expressions;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
namespace Serein.Library.Network.WebSocketCommunication.Handle
{
public class SocketMsgHandleHelper
{
/// <summary>
/// (Theme Name ,Data Name) - HandleModule
/// </summary>
public ConcurrentDictionary<(string, string), MyHandleModule> MyHandleModuleDict
= new ConcurrentDictionary<(string, string), MyHandleModule>();
private MyHandleModule AddMyHandleModule(string themeKeyName, string dataKeyName)
{
var key = (themeKeyName, dataKeyName);
if (!MyHandleModuleDict.TryGetValue(key, out var myHandleModule))
{
myHandleModule = new MyHandleModule(themeKeyName, dataKeyName);
MyHandleModuleDict[key] = myHandleModule;
}
return myHandleModule;
}
public void RemoteModule(ISocketControlBase socketControlBase)
{
var type = socketControlBase.GetType();
var moduleAttribute = type.GetCustomAttribute<AutoSocketModuleAttribute>();
if (moduleAttribute is null)
{
return;
}
var themeKeyName = moduleAttribute.JsonThemeField;
var dataKeyName = moduleAttribute.JsonDataField;
var key = (themeKeyName, dataKeyName);
if (MyHandleModuleDict.TryRemove(key, out var myHandleModules))
{
myHandleModules.ResetConfig(socketControlBase);
}
}
public void AddModule(ISocketControlBase socketControlBase)
{
var type = socketControlBase.GetType();
var moduleAttribute = type.GetCustomAttribute<AutoSocketModuleAttribute>();
if (moduleAttribute is null)
{
return;
}
// 添加处理模块
var themeKey = moduleAttribute.JsonThemeField;
var dataKey = moduleAttribute.JsonDataField;
var handlemodule = AddMyHandleModule(themeKey, dataKey);
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Select(method =>
{
var methodsAttribute = method.GetCustomAttribute<AutoSocketHandleAttribute>();
if (methodsAttribute is null)
{
return (string.Empty, null);
}
else
{
var value = methodsAttribute.ThemeValue;
return (value, method);
}
})
.Where(x => !string.IsNullOrEmpty(x.value)).ToList();
if (methods.Count == 0)
{
return;
}
foreach ((var value, var method) in methods)
{
handlemodule.AddHandleConfigs(value, socketControlBase, method);
}
}
public async Task HandleMsgAsync(Func<string, Task> RecoverAsync, string message)
{
JObject json = JObject.Parse(message);
await Task.Run(() =>
{
foreach (var module in MyHandleModuleDict.Values)
{
module.HandleSocketMsg(RecoverAsync, json);
}
});
}
}
}

View File

@@ -0,0 +1,41 @@
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;
namespace Serein.Library.Network.WebSocketCommunication
{
public interface ISocketControlBase
{
Guid HandleGuid { get; }
}
//[AutoRegister(RegisterSequence.FlowLoading)]
//[AutoSocketModule(JsonThemeField = "theme", JsonDataField = "data")]
//public class UserService : ISocketControlBase
//{
// public Guid HandleGuid { get; } = new Guid();
// // Action<string> 类型是特殊的,会用一个委托代替,这个委托可以将文本信息发送到客户端
// // Action<object> 类型是特殊的会用一个委托代替这个委托可以将对象转成json发送到客户端
// [AutoSocketHandle]
// public void AddUser(User user,Action<string> Recover)
// {
// Console.WriteLine(user.ToString());
// Recover("ok");
// }
// [AutoSocketHandle(ThemeValue = "Remote")]
// public void DeleteUser(User user, Action<string> Recover)
// {
// Console.WriteLine(user.ToString());
// }
//}
}

View File

@@ -0,0 +1,136 @@
using Newtonsoft.Json.Linq;
using Serein.Library.Attributes;
using Serein.Library.Web;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication
{
[AutoRegister]
public class WebSocketClient
{
public WebSocketClient()
{
}
private ClientWebSocket _client = new ClientWebSocket();
public async Task ConnectAsync(string uri)
{
await _client.ConnectAsync(new Uri(uri), CancellationToken.None);
await ReceiveAsync();
}
public async Task SendAsync(WebSocket webSocket,string message)
{
var buffer = Encoding.UTF8.GetBytes(message);
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
private async Task ReceiveAsync()
{
var buffer = new byte[1024];
while (_client.State == WebSocketState.Open)
{
try
{
var result = await _client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await _client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}
else
{
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
Debug.WriteLine($"Received: {message}");
}
}
catch (Exception ex)
{
await Console.Out.WriteLineAsync(ex.ToString());
}
}
}
}
/* #region 消息处理
private readonly string ThemeField;
private readonly ConcurrentDictionary<string, HandldConfig> ThemeConfigs = new ConcurrentDictionary<string, HandldConfig>();
public async Task HandleSocketMsg(string jsonStr)
{
JObject json;
try
{
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
{
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*/
}

View File

@@ -1,10 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Serein.Library.Network.WebSocketCommunication
{
public class WebSocketRouter
{
}
}

View File

@@ -1,57 +1,103 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serein.Library.Attributes;
using Serein.Library.Network.WebSocketCommunication.Handle;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.WebSockets;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication
{
[AutoRegister]
public class WebSocketServer
{
public Func<string,Action> OnReceiveMsg;
public WebSocketServer()
{
}
public SocketMsgHandleHelper MsgHandleHelper { get; } = new SocketMsgHandleHelper();
HttpListener listener;
public async Task StartAsync(string url)
{
HttpListener listener = new HttpListener();
listener = new HttpListener();
listener.Prefixes.Add(url);
listener.Start();
while (true)
{
var context = await listener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
try
{
var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接
_ = HandleWebSocketAsync(webSocketContext.WebSocket); // 处理消息
var context = await listener.GetContextAsync();
string clientPoint = context.Request.RemoteEndPoint?.ToString();
await Console.Out.WriteLineAsync($"新的连接加入:{clientPoint}");
if (context.Request.IsWebSocketRequest)
{
var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接
_ = HandleWebSocketAsync(webSocketContext.WebSocket); // 处理消息
}
}
catch (Exception ex)
{
await Console.Out.WriteLineAsync(ex.Message);
break;
}
}
}
public void Stop()
{
listener?.Stop();
}
private async Task HandleWebSocketAsync(WebSocket webSocket)
{
Func<string,Task> SendAsync = async (text) =>
{
await WebSocketServer.SendAsync(webSocket, text);
};
var buffer = new byte[1024];
while (webSocket.State == WebSocketState.Open)
{
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
SendAsync = null;
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}
else
{
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Received: {message}");
var action = OnReceiveMsg.Invoke(message);
action?.Invoke();
// 回显消息(可选)
//ar echoMessage = Encoding.UTF8.GetBytes(message);
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message);
//foreach (var item in HandldHelpers)
//{
// await item.HandleSocketMsg(webSocket, message);
//}
//Console.WriteLine($"Received: {message}");
//var echoMessage = Encoding.UTF8.GetBytes(message);
//await webSocket.SendAsync(new ArraySegment<byte>(echoMessage, 0, echoMessage.Length), result.MessageType, result.EndOfMessage, CancellationToken.None);
}
}
}
public static async Task SendAsync(WebSocket webSocket, string message)
{
var buffer = Encoding.UTF8.GetBytes(message);
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
}