mirror of
https://github.com/snltty/linker.git
synced 2025-10-06 01:26:54 +08:00
291 lines
9.7 KiB
C#
291 lines
9.7 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// 前段接口服务
|
|
/// </summary>
|
|
public class ApiServer : IApiServer
|
|
{
|
|
protected readonly Dictionary<string, PluginPathCacheInfo> plugins = new();
|
|
protected readonly ConcurrentDictionary<uint, ConnectionTimeInfo> connectionTimes = new();
|
|
public uint OnlineNum = 0;
|
|
private Memory<byte> password = Helper.EmptyArray;
|
|
|
|
private WebSocketServer server;
|
|
|
|
public ApiServer()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// 开启websockt
|
|
/// </summary>
|
|
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<ApiControllerRequestInfo>();
|
|
req.Connection = connection;
|
|
OnMessage(req).ContinueWith((result) =>
|
|
{
|
|
var resp = result.Result.ToJson().ToBytes();
|
|
connection.SendFrameText(resp);
|
|
});
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 收到消息
|
|
/// </summary>
|
|
/// <param name="model"></param>
|
|
/// <returns></returns>
|
|
public async Task<ApiControllerResponseInfo> 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($"{model.Path} -> {ex.Message}");
|
|
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<byte> content)
|
|
{
|
|
if (server.Connections.Any())
|
|
{
|
|
try
|
|
{
|
|
Memory<byte> 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<byte>.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<byte>.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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 前段接口缓存
|
|
/// </summary>
|
|
public struct PluginPathCacheInfo
|
|
{
|
|
/// <summary>
|
|
/// 对象
|
|
/// </summary>
|
|
public object Target { get; set; }
|
|
/// <summary>
|
|
/// 方法
|
|
/// </summary>
|
|
public MethodInfo Method { get; set; }
|
|
/// <summary>
|
|
/// 是否void
|
|
/// </summary>
|
|
public bool IsVoid { get; set; }
|
|
/// <summary>
|
|
/// 是否task
|
|
/// </summary>
|
|
public bool IsTask { get; set; }
|
|
/// <summary>
|
|
/// 是否task result
|
|
/// </summary>
|
|
public bool IsTaskResult { get; set; }
|
|
}
|
|
}
|