using Linker.Libs.Extends; using Linker.Libs.Websocket; using System; using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace Linker.Libs.Api { /// /// 前段接口服务 /// public class ApiServer : IApiServer { protected readonly Dictionary plugins = new(); protected readonly ConcurrentDictionary connectionTimes = new(); public uint OnlineNum = 0; private Memory password = Helper.EmptyArray; private WebSocketServer server; public ApiServer() { } /// /// 开启websockt /// public void Websocket(int port, string password = "") { this.password = Encoding.UTF8.GetBytes(password); server = new WebSocketServer(); try { server.Start(System.Net.IPAddress.Any, port); } catch (Exception ex) { LoggerHelper.Instance.Error(ex); } server.OnConnecting = (connection, header) => { bool res = this.password.Length == 0 || this.password.Span.SequenceEqual(header.SecWebSocketProtocol.Span); if (res) { header.SecWebSocketExtensions = Helper.EmptyArray; } return res; }; server.OnOpen = (connection) => { Interlocked.Increment(ref OnlineNum); connectionTimes.TryAdd(connection.Id, new ConnectionTimeInfo()); }; server.OnDisConnectd = (connection) => { Interlocked.Decrement(ref OnlineNum); if (OnlineNum < 0) Interlocked.Exchange(ref OnlineNum, 0); connectionTimes.TryRemove(connection.Id, out _); }; server.OnMessage = (connection, frame, message) => { if (connectionTimes.TryGetValue(connection.Id, out ConnectionTimeInfo timeInfo)) { timeInfo.DateTime = DateTime.Now; } var req = message.DeJson(); req.Connection = connection; OnMessage(req).ContinueWith((result) => { var resp = result.Result.ToJson().ToBytes(); connection.SendFrameText(resp); }); }; } /// /// 收到消息 /// /// /// public async Task OnMessage(ApiControllerRequestInfo model) { model.Path = model.Path.ToLower(); if (plugins.TryGetValue(model.Path, out PluginPathCacheInfo plugin) == false) { return new ApiControllerResponseInfo { Content = "not exists this path", RequestId = model.RequestId, Path = model.Path, Code = ApiControllerResponseCodes.NotFound }; } try { ApiControllerParamsInfo param = new ApiControllerParamsInfo { RequestId = model.RequestId, Content = model.Content, Connection = model.Connection }; dynamic resultAsync = plugin.Method.Invoke(plugin.Target, new object[] { param }); object resultObject = null; if (plugin.IsVoid == false) { if (plugin.IsTask) { await resultAsync.ConfigureAwait(false); if (plugin.IsTaskResult) { resultObject = resultAsync.Result; } } else { resultObject = resultAsync; } } return new ApiControllerResponseInfo { Code = param.Code, Content = param.Code != ApiControllerResponseCodes.Error ? resultObject : param.ErrorMessage, RequestId = model.RequestId, Path = model.Path, }; } catch (Exception ex) { LoggerHelper.Instance.Error(ex); return new ApiControllerResponseInfo { Content = ex.Message, RequestId = model.RequestId, Path = model.Path, Code = ApiControllerResponseCodes.Error }; } } public void Notify(string path, object content) { if (server.Connections.Any()) { try { byte[] bytes = JsonSerializer.Serialize(new ApiControllerResponseInfo { Code = ApiControllerResponseCodes.Success, Content = content, Path = path, RequestId = 0 }).ToBytes(); foreach (WebsocketConnection connection in server.Connections) { if (connection.Connected && connectionTimes.TryGetValue(connection.Id, out ConnectionTimeInfo timeInfo) && (DateTime.Now - timeInfo.DateTime).TotalMilliseconds < 1000) { try { connection.SendFrameText(bytes); } catch (Exception) { } } } } catch (Exception ex) { LoggerHelper.Instance.Error(ex); } } } public void Notify(string path, string name, ReadOnlyMemory content) { if (server.Connections.Any()) { try { Memory headMemory = JsonSerializer.Serialize(new ApiControllerResponseInfo { Code = ApiControllerResponseCodes.Success, Content = name, Path = path, RequestId = 0 }).ToBytes(); int length = 4 + headMemory.Length + content.Length; byte[] result = ArrayPool.Shared.Rent(length); int index = 0; headMemory.Length.ToBytes(result); index += 4; headMemory.CopyTo(result.AsMemory(index)); index += headMemory.Length; content.CopyTo(result.AsMemory(index)); index += content.Length; foreach (WebsocketConnection connection in server.Connections) { if (connection.Connected && connectionTimes.TryGetValue(connection.Id, out ConnectionTimeInfo timeInfo) && (DateTime.Now - timeInfo.DateTime).TotalMilliseconds < 1000) { try { connection.SendFrameBinary(result.AsMemory(0, length)); } catch (Exception) { } } } ArrayPool.Shared.Return(result); } catch (Exception) { //LoggerHelper.Instance.Error(ex); } } } public void Notify(string path, object content, WebsocketConnection connection) { try { if (connection.Connected == false) return; byte[] bytes = JsonSerializer.Serialize(new ApiControllerResponseInfo { Code = ApiControllerResponseCodes.Success, Content = content, Path = path, RequestId = 0 }).ToBytes(); try { connection.SendFrameText(bytes); } catch (Exception) { } } catch (Exception) { //LoggerHelper.Instance.Error(ex); } } } public sealed class ConnectionTimeInfo { public DateTime DateTime { get; set; } = DateTime.Now; } /// /// 前段接口缓存 /// public struct PluginPathCacheInfo { /// /// 对象 /// public object Target { get; set; } /// /// 方法 /// public MethodInfo Method { get; set; } /// /// 是否void /// public bool IsVoid { get; set; } /// /// 是否task /// public bool IsTask { get; set; } /// /// 是否task result /// public bool IsTaskResult { get; set; } } }