mirror of
https://github.com/snltty/linker.git
synced 2025-12-20 02:16:44 +08:00
init
This commit is contained in:
70
common.libs/CommandHelper.cs
Normal file
70
common.libs/CommandHelper.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace common.libs
|
||||
{
|
||||
public sealed class CommandHelper
|
||||
{
|
||||
public static string Windows(string arg, string[] commands)
|
||||
{
|
||||
return Execute("cmd.exe", arg, commands);
|
||||
}
|
||||
public static string Linux(string arg, string[] commands)
|
||||
{
|
||||
return Execute("/bin/bash", arg, commands);
|
||||
}
|
||||
public static string Osx(string arg, string[] commands)
|
||||
{
|
||||
return Execute("/bin/bash", arg, commands);
|
||||
}
|
||||
public static Process Execute(string fileName, string arg)
|
||||
{
|
||||
Process proc = new Process();
|
||||
proc.StartInfo.CreateNoWindow = true;
|
||||
proc.StartInfo.FileName = fileName;
|
||||
proc.StartInfo.UseShellExecute = false;
|
||||
proc.StartInfo.RedirectStandardError = true;
|
||||
proc.StartInfo.RedirectStandardInput = true;
|
||||
proc.StartInfo.RedirectStandardOutput = true;
|
||||
proc.StartInfo.Arguments = arg;
|
||||
proc.StartInfo.Verb = "runas";
|
||||
proc.Start();
|
||||
|
||||
//Process proc = Process.Start(fileName, arg);
|
||||
return proc;
|
||||
}
|
||||
public static string Execute(string fileName, string arg, string[] commands)
|
||||
{
|
||||
Process proc = new Process();
|
||||
proc.StartInfo.WorkingDirectory = Path.GetFullPath(Path.Join("./"));
|
||||
proc.StartInfo.CreateNoWindow = true;
|
||||
proc.StartInfo.FileName = fileName;
|
||||
proc.StartInfo.UseShellExecute = false;
|
||||
proc.StartInfo.RedirectStandardError = true;
|
||||
proc.StartInfo.RedirectStandardInput = true;
|
||||
proc.StartInfo.RedirectStandardOutput = true;
|
||||
proc.StartInfo.Arguments = arg;
|
||||
proc.StartInfo.Verb = "runas";
|
||||
proc.Start();
|
||||
|
||||
if (commands.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < commands.Length; i++)
|
||||
{
|
||||
proc.StandardInput.WriteLine(commands[i]);
|
||||
}
|
||||
}
|
||||
|
||||
proc.StandardInput.AutoFlush = true;
|
||||
proc.StandardInput.WriteLine("exit");
|
||||
proc.StandardInput.Close();
|
||||
string output = proc.StandardOutput.ReadToEnd();
|
||||
string error = proc.StandardError.ReadToEnd();
|
||||
proc.WaitForExit();
|
||||
proc.Close();
|
||||
proc.Dispose();
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
common.libs/EnumBufferSize.cs
Normal file
17
common.libs/EnumBufferSize.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace common.libs
|
||||
{
|
||||
public enum EnumBufferSize : byte
|
||||
{
|
||||
KB_1 = 0,
|
||||
KB_2 = 1,
|
||||
KB_4 = 2,
|
||||
KB_8 = 3,
|
||||
KB_16 = 4,
|
||||
KB_32 = 5,
|
||||
KB_64 = 6,
|
||||
KB_128 = 7,
|
||||
KB_256 = 8,
|
||||
KB_512 = 9,
|
||||
KB_1024 = 10,
|
||||
}
|
||||
}
|
||||
27
common.libs/GCHelper.cs
Normal file
27
common.libs/GCHelper.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace common.libs
|
||||
{
|
||||
public static class GCHelper
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool SetProcessWorkingSetSize(IntPtr proc, int min, int max);
|
||||
public static void FlushMemory()
|
||||
{
|
||||
GC.Collect();
|
||||
GC.SuppressFinalize(true);
|
||||
GC.WaitForPendingFinalizers();
|
||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||
{
|
||||
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);
|
||||
}
|
||||
}
|
||||
public static void Gc(object obj)
|
||||
{
|
||||
GC.Collect();
|
||||
GC.SuppressFinalize(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
common.libs/Helper.cs
Normal file
21
common.libs/Helper.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace common.libs
|
||||
{
|
||||
public static class Helper
|
||||
{
|
||||
public static byte[] EmptyArray = Array.Empty<byte>();
|
||||
public static byte[] TrueArray = new byte[] { 1 };
|
||||
public static byte[] FalseArray = new byte[] { 0 };
|
||||
|
||||
public static async Task Await()
|
||||
{
|
||||
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
AppDomain.CurrentDomain.ProcessExit += (sender, e) => cancellationTokenSource.Cancel();
|
||||
Console.CancelKeyPress += (sender, e) => cancellationTokenSource.Cancel();
|
||||
await Task.Delay(-1, cancellationTokenSource.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
123
common.libs/Logger.cs
Normal file
123
common.libs/Logger.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace common.libs
|
||||
{
|
||||
public sealed class Logger
|
||||
{
|
||||
private static readonly Lazy<Logger> lazy = new Lazy<Logger>(() => new Logger());
|
||||
public static Logger Instance => lazy.Value;
|
||||
|
||||
private readonly ConcurrentQueue<LoggerModel> queue = new ConcurrentQueue<LoggerModel>();
|
||||
public Action<LoggerModel> OnLogger { get; set; } = (param) => { };
|
||||
|
||||
public int PaddingWidth { get; set; } = 50;
|
||||
#if DEBUG
|
||||
public LoggerTypes LoggerLevel { get; set; } = LoggerTypes.DEBUG;
|
||||
#else
|
||||
public LoggerTypes LoggerLevel { get; set; } = LoggerTypes.WARNING;
|
||||
#endif
|
||||
|
||||
private Logger()
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
if (queue.TryDequeue(out LoggerModel model))
|
||||
{
|
||||
OnLogger?.Invoke(model);
|
||||
}
|
||||
}
|
||||
Thread.Sleep(15);
|
||||
}
|
||||
})
|
||||
{ IsBackground = true }.Start();
|
||||
}
|
||||
|
||||
|
||||
public int lockNum = 0;
|
||||
public void Lock()
|
||||
{
|
||||
Interlocked.Increment(ref lockNum);
|
||||
}
|
||||
public void UnLock()
|
||||
{
|
||||
Interlocked.Decrement(ref lockNum);
|
||||
}
|
||||
|
||||
public void Debug(string content, params object[] args)
|
||||
{
|
||||
if (args != null && args.Length > 0)
|
||||
{
|
||||
content = string.Format(content, args);
|
||||
}
|
||||
Enqueue(new LoggerModel { Type = LoggerTypes.DEBUG, Content = content });
|
||||
}
|
||||
public void Info(string content, params object[] args)
|
||||
{
|
||||
if (args != null && args.Length > 0)
|
||||
{
|
||||
content = string.Format(content, args);
|
||||
}
|
||||
Enqueue(new LoggerModel { Type = LoggerTypes.INFO, Content = content });
|
||||
}
|
||||
public void Warning(string content, params object[] args)
|
||||
{
|
||||
if (args != null && args.Length > 0)
|
||||
{
|
||||
content = string.Format(content, args);
|
||||
}
|
||||
Enqueue(new LoggerModel { Type = LoggerTypes.WARNING, Content = content });
|
||||
}
|
||||
public void Error(string content, params object[] args)
|
||||
{
|
||||
if (args != null && args.Length > 0)
|
||||
{
|
||||
content = string.Format(content, args);
|
||||
}
|
||||
Enqueue(new LoggerModel { Type = LoggerTypes.ERROR, Content = content });
|
||||
}
|
||||
public void Error(Exception ex)
|
||||
{
|
||||
Enqueue(new LoggerModel { Type = LoggerTypes.ERROR, Content = ex + "" });
|
||||
}
|
||||
|
||||
public void FATAL(string content, params object[] args)
|
||||
{
|
||||
if (args != null && args.Length > 0)
|
||||
{
|
||||
content = string.Format(content, args);
|
||||
}
|
||||
Enqueue(new LoggerModel { Type = LoggerTypes.FATAL, Content = content });
|
||||
}
|
||||
public void FATAL(Exception ex)
|
||||
{
|
||||
Enqueue(new LoggerModel { Type = LoggerTypes.FATAL, Content = ex + "" });
|
||||
}
|
||||
|
||||
public void Enqueue(LoggerModel model)
|
||||
{
|
||||
queue.Enqueue(model);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class LoggerModel
|
||||
{
|
||||
public LoggerTypes Type { get; set; } = LoggerTypes.INFO;
|
||||
public DateTime Time { get; set; } = DateTime.Now;
|
||||
public string Content { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public enum LoggerTypes : byte
|
||||
{
|
||||
DEBUG = 0,
|
||||
INFO = 1,
|
||||
WARNING = 2,
|
||||
ERROR = 3,
|
||||
FATAL = 4,
|
||||
}
|
||||
}
|
||||
50
common.libs/NetworkHelper.cs
Normal file
50
common.libs/NetworkHelper.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace common.libs
|
||||
{
|
||||
public static class NetworkHelper
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 域名解析
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
/// <returns></returns>
|
||||
public static IPAddress GetDomainIp(string domain)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(domain)) return null;
|
||||
if (IPAddress.TryParse(domain, out IPAddress ip))
|
||||
{
|
||||
return ip;
|
||||
}
|
||||
IPAddress[] list = Dns.GetHostEntry(domain).AddressList;
|
||||
if (list.Length > 0) return list[0];
|
||||
return null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
#if DISABLE_IPV6 || (!UNITY_EDITOR && ENABLE_IL2CPP && !UNITY_2018_3_OR_NEWER)
|
||||
public static bool IPv6Support = false;
|
||||
#elif !UNITY_2019_1_OR_NEWER && !UNITY_2018_4_OR_NEWER && (!UNITY_EDITOR && ENABLE_IL2CPP && UNITY_2018_3_OR_NEWER)
|
||||
public static bool IPv6Support = Socket.OSSupportsIPv6 && int.Parse(UnityEngine.Application.unityVersion.Remove(UnityEngine.Application.unityVersion.IndexOf('f')).Split('.')[2]) >= 6;
|
||||
#elif UNITY_2018_2_OR_NEWER
|
||||
public static bool IPv6Support = Socket.OSSupportsIPv6;
|
||||
#elif UNITY
|
||||
#pragma warning disable 618
|
||||
public static bool IPv6Support = Socket.SupportsIPv6;
|
||||
#pragma warning restore 618
|
||||
#else
|
||||
|
||||
public static bool IPv6Support = Socket.OSSupportsIPv6;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
79
common.libs/NumberSpace.cs
Normal file
79
common.libs/NumberSpace.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System.Threading;
|
||||
|
||||
namespace common.libs
|
||||
{
|
||||
public sealed class NumberSpace
|
||||
{
|
||||
private ulong num;
|
||||
|
||||
public NumberSpace(ulong defaultVal = 0)
|
||||
{
|
||||
num = defaultVal;
|
||||
}
|
||||
|
||||
public ulong Increment()
|
||||
{
|
||||
Interlocked.CompareExchange(ref num, 0, ulong.MaxValue - 10000);
|
||||
Interlocked.Increment(ref num);
|
||||
return num;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NumberSpaceUInt32
|
||||
{
|
||||
private uint num = 0;
|
||||
|
||||
public NumberSpaceUInt32(uint defaultVal = 0)
|
||||
{
|
||||
num = defaultVal;
|
||||
}
|
||||
|
||||
public uint Get()
|
||||
{
|
||||
return num;
|
||||
}
|
||||
|
||||
public uint Increment()
|
||||
{
|
||||
Interlocked.CompareExchange(ref num, 0, uint.MaxValue - 10000);
|
||||
Interlocked.Increment(ref num);
|
||||
return num;
|
||||
}
|
||||
|
||||
public void Decrement()
|
||||
{
|
||||
Interlocked.Decrement(ref num);
|
||||
}
|
||||
|
||||
public void Reset(uint val = 0)
|
||||
{
|
||||
Interlocked.Exchange(ref num, val);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BoolSpace
|
||||
{
|
||||
bool _default;
|
||||
private bool value;
|
||||
public BoolSpace(bool defaultVal = true)
|
||||
{
|
||||
_default = defaultVal;
|
||||
value = _default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否是原始值
|
||||
/// </summary>
|
||||
public bool IsDefault => value == _default;
|
||||
public bool Reverse()
|
||||
{
|
||||
value = !_default;
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
value = _default;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
common.libs/ReceiveDataBuffer.cs
Normal file
99
common.libs/ReceiveDataBuffer.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
|
||||
namespace common.libs
|
||||
{
|
||||
public sealed class ReceiveDataBuffer
|
||||
{
|
||||
private Memory<byte> items { get; set; }
|
||||
private int size = 0;
|
||||
public int Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return size;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
items = Helper.EmptyArray;
|
||||
}
|
||||
else if (value > items.Length)
|
||||
{
|
||||
Memory<byte> newItems = new byte[value].AsMemory();
|
||||
items.CopyTo(newItems);
|
||||
items = newItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
public Memory<byte> Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return items;
|
||||
}
|
||||
}
|
||||
public void AddRange(Memory<byte> data)
|
||||
{
|
||||
if(data.Length > 0)
|
||||
{
|
||||
BeResize(data.Length);
|
||||
|
||||
data.CopyTo(items.Slice(size, data.Length));
|
||||
size += data.Length;
|
||||
}
|
||||
}
|
||||
public void AddRange(Memory<byte> data, int length)
|
||||
{
|
||||
if (length > 0)
|
||||
{
|
||||
BeResize(length);
|
||||
|
||||
data.Slice(0, length).CopyTo(items.Slice(size, length));
|
||||
size += length;
|
||||
}
|
||||
}
|
||||
public void AddRange(byte[] data, int offset, int length)
|
||||
{
|
||||
if(length > 0)
|
||||
{
|
||||
BeResize(length);
|
||||
data.AsMemory(offset, length).CopyTo(items.Slice(size, length));
|
||||
size += length;
|
||||
}
|
||||
}
|
||||
public void RemoveRange(int index, int count)
|
||||
{
|
||||
if (index >= 0 && count > 0 && size - index >= count)
|
||||
{
|
||||
size -= count;
|
||||
if (index < size)
|
||||
{
|
||||
items.Slice(index + count, size - index).CopyTo(items.Slice(index, size - index));
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Clear(bool clearData = false)
|
||||
{
|
||||
size = 0;
|
||||
if (clearData)
|
||||
{
|
||||
Size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void BeResize(int length)
|
||||
{
|
||||
int _size = size + length;
|
||||
if (_size > items.Length)
|
||||
{
|
||||
int newsize = items.Length * 2;
|
||||
if (newsize < _size)
|
||||
{
|
||||
newsize = _size;
|
||||
}
|
||||
Size = newsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
common.libs/ReflectionHelper.cs
Normal file
32
common.libs/ReflectionHelper.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace common.libs
|
||||
{
|
||||
public sealed class ReflectionHelper
|
||||
{
|
||||
public static IEnumerable<Type> GetInterfaceSchieves(Type type)
|
||||
{
|
||||
return GetInterfaceSchieves(AppDomain.CurrentDomain.GetAssemblies(), type);
|
||||
}
|
||||
public static IEnumerable<Type> GetInterfaceSchieves(Assembly[] assemblys, Type type)
|
||||
{
|
||||
return assemblys.SelectMany(c => c.GetTypes())
|
||||
.Where(c => !c.IsAbstract).Where(c => c.GetInterfaces().Contains(type));
|
||||
}
|
||||
public static IEnumerable<Type> GetSubClass(Assembly[] assemblys, Type type)
|
||||
{
|
||||
return assemblys.SelectMany(c => c.GetTypes())
|
||||
.Where(c => !c.IsAbstract).Where(c => c.IsSubclassOf(type));
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<Type> GetEnums(Assembly[] assemblys)
|
||||
{
|
||||
return assemblys.SelectMany(c => c.GetTypes())
|
||||
.Where(c => c.IsEnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
209
common.libs/WheelTimer.cs
Normal file
209
common.libs/WheelTimer.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace common.libs
|
||||
{
|
||||
/// <summary>
|
||||
/// 时间轮延时任务
|
||||
/// </summary>
|
||||
public sealed class WheelTimer<T>
|
||||
{
|
||||
//流转次数,
|
||||
long ticks = 0;
|
||||
//槽数
|
||||
int bucketLength = 2 << 8;
|
||||
//mask = bucketLength-1;ticks & mask; 获得槽下标
|
||||
int mask = 0;
|
||||
//精度,应使用Thread.Sleep(1)时间的倍数
|
||||
int tickDurationMs = 30;
|
||||
//槽
|
||||
WheelTimerBucket<T>[] buckets = Array.Empty<WheelTimerBucket<T>>();
|
||||
//先入列,等待入槽
|
||||
ConcurrentQueue<WheelTimerTimeout<T>> timeouts = new ConcurrentQueue<WheelTimerTimeout<T>>();
|
||||
AutoResetEvent autoReset = new AutoResetEvent(true);
|
||||
|
||||
public WheelTimer()
|
||||
{
|
||||
CreateBuckets();
|
||||
Worker();
|
||||
}
|
||||
public WheelTimerTimeout<T> NewTimeout(WheelTimerTimeoutTask<T> task, int delayMs, bool reuse = false)
|
||||
{
|
||||
if (task == null)
|
||||
{
|
||||
throw new ArgumentNullException("task must be not null");
|
||||
}
|
||||
if (delayMs <= 1)
|
||||
{
|
||||
throw new ArgumentNullException("delayMs must be > 1 ms");
|
||||
}
|
||||
|
||||
WheelTimerTimeout<T> timeout = new WheelTimerTimeout<T> { Delay = delayMs, Task = task, Reuse = reuse };
|
||||
timeouts.Enqueue(timeout);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
private void CreateBuckets()
|
||||
{
|
||||
mask = bucketLength - 1;
|
||||
buckets = new WheelTimerBucket<T>[bucketLength];
|
||||
for (int i = 0; i < buckets.Length; i++)
|
||||
{
|
||||
buckets[i] = new WheelTimerBucket<T>();
|
||||
}
|
||||
}
|
||||
|
||||
double ticksMore = 0;
|
||||
private void Worker()
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
for (; ; )
|
||||
{
|
||||
long start = DateTime.UtcNow.Ticks;
|
||||
//等下一个时间点
|
||||
WaitForNextTick();
|
||||
//待入槽队列入槽
|
||||
TransferTimeoutsToBuckets();
|
||||
//执行当前槽的任务
|
||||
ExpireTimeouts(buckets[(ticks & mask)]);
|
||||
ticks++;
|
||||
ticksMore += (DateTime.UtcNow.Ticks - start) / TimeSpan.TicksPerMillisecond - tickDurationMs;
|
||||
|
||||
double forwardCount = (ticksMore / tickDurationMs);
|
||||
while (forwardCount > 1)
|
||||
{
|
||||
ticksMore -= tickDurationMs;
|
||||
|
||||
start = DateTime.UtcNow.Ticks;
|
||||
//待入槽队列入槽
|
||||
TransferTimeoutsToBuckets();
|
||||
//执行当前槽的任务
|
||||
ExpireTimeouts(buckets[(ticks & mask)]);
|
||||
ticks++;
|
||||
|
||||
ticksMore += (DateTime.UtcNow.Ticks - start) / TimeSpan.TicksPerMillisecond;
|
||||
|
||||
forwardCount = ticksMore / tickDurationMs;
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
{ IsBackground = true }.Start();
|
||||
}
|
||||
private void WaitForNextTick()
|
||||
{
|
||||
autoReset.WaitOne(tickDurationMs);
|
||||
}
|
||||
private void TransferTimeoutsToBuckets()
|
||||
{
|
||||
if (timeouts.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//一次最多转移100000个
|
||||
for (int i = 0; i < 100000; i++)
|
||||
{
|
||||
if (timeouts.TryDequeue(out WheelTimerTimeout<T> timeout) == false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (timeout.IsCanceled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//所需格子数
|
||||
int expireTicks = (int)((timeout.Delay + ticksMore) / tickDurationMs);
|
||||
//所需轮次
|
||||
timeout.Rounds = expireTicks / buckets.Length;
|
||||
//除轮次外,剩下的格子数应在哪个槽中
|
||||
int stopIndex = ((int)(ticks & mask) + (expireTicks - timeout.Rounds * buckets.Length)) & mask;
|
||||
|
||||
buckets[stopIndex].AddTimeout(timeout);
|
||||
}
|
||||
}
|
||||
private void ExpireTimeouts(WheelTimerBucket<T> bucket)
|
||||
{
|
||||
LinkedListNode<WheelTimerTimeout<T>> timeout = bucket.List.First;
|
||||
while (timeout != null)
|
||||
{
|
||||
bool remove = false;
|
||||
if (timeout.Value.Rounds <= 0 && timeout.Value.IsCanceled == false)
|
||||
{
|
||||
remove = true;
|
||||
try
|
||||
{
|
||||
timeout.Value.Task.Callback?.Invoke(timeout.Value);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
else if (timeout.Value.IsCanceled)
|
||||
{
|
||||
remove = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeout.Value.Rounds--;
|
||||
}
|
||||
|
||||
LinkedListNode<WheelTimerTimeout<T>> next = timeout.Next;
|
||||
if (remove)
|
||||
{
|
||||
bucket.Remove(timeout);
|
||||
if (timeout.Value.Reuse && !timeout.Value.IsCanceled)
|
||||
{
|
||||
timeouts.Enqueue(timeout.Value);
|
||||
}
|
||||
}
|
||||
timeout = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class WheelTimerBucket<T>
|
||||
{
|
||||
public LinkedList<WheelTimerTimeout<T>> List { get; private set; } = new LinkedList<WheelTimerTimeout<T>>();
|
||||
public void AddTimeout(WheelTimerTimeout<T> timeout)
|
||||
{
|
||||
List.AddLast(timeout);
|
||||
}
|
||||
public void Remove(LinkedListNode<WheelTimerTimeout<T>> node)
|
||||
{
|
||||
List.Remove(node);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class WheelTimerTimeout<T>
|
||||
{
|
||||
public int Delay { get; init; } = 0;
|
||||
public int Rounds { get; set; } = 0;
|
||||
public bool Reuse { get; init; } = false;
|
||||
public WheelTimerTimeoutTask<T> Task { get; init; }
|
||||
public TimeoutState State { get; private set; } = TimeoutState.Normal;
|
||||
public bool IsCanceled => State == TimeoutState.Canceld;
|
||||
public void Cancel()
|
||||
{
|
||||
State = TimeoutState.Canceld;
|
||||
}
|
||||
|
||||
public enum TimeoutState
|
||||
{
|
||||
Normal = 1 << 0,
|
||||
Canceld = 1 << 1,
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class WheelTimerTimeoutTask<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 保存状态数据
|
||||
/// </summary>
|
||||
public T State { get; init; }
|
||||
/// <summary>
|
||||
/// 不要抛异常影响轮转时间
|
||||
/// </summary>
|
||||
public Action<WheelTimerTimeout<T>> Callback { get; init; }
|
||||
}
|
||||
}
|
||||
23
common.libs/WindowHelper.cs
Normal file
23
common.libs/WindowHelper.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace common.libs
|
||||
{
|
||||
public class WindowHelper
|
||||
{
|
||||
#region 进程
|
||||
public static Process[] processes;
|
||||
public static void UpdateCurrentProcesses()
|
||||
{
|
||||
processes = Process.GetProcesses();
|
||||
}
|
||||
public static bool GetHasWindowByName(string name)
|
||||
{
|
||||
return processes != null && processes.Any(c => c.ProcessName.Equals(name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
38
common.libs/common.libs.csproj
Normal file
38
common.libs/common.libs.csproj
Normal file
@@ -0,0 +1,38 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<Configurations>Debug;Release;ReleaseLinux</Configurations>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<PublishAot>false</PublishAot>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DefineConstants>TRACE;RELEASE</DefineConstants>
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLinux|AnyCPU'">
|
||||
<DefineConstants>TRACE;RELEASE</DefineConstants>
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(VisualStudioVersion)'>='17.0'">
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0-preview.7.22375.6" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(VisualStudioVersion)'<'17.0'">
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
110
common.libs/database/IConfigDataProvider.cs
Normal file
110
common.libs/database/IConfigDataProvider.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using common.libs.extends;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace common.libs.database
|
||||
{
|
||||
/// <summary>
|
||||
/// 配置文件缓存
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IConfigDataProvider<T> where T : class, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<T> Load();
|
||||
/// <summary>
|
||||
/// 加载
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<string> LoadString();
|
||||
/// <summary>
|
||||
/// 保存
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
Task Save(T model);
|
||||
/// <summary>
|
||||
/// 保存
|
||||
/// </summary>
|
||||
/// <param name="jsonStr"></param>
|
||||
/// <returns></returns>
|
||||
Task Save(string jsonStr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置文件的文件缓存
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public sealed class ConfigDataFileProvider<T> : IConfigDataProvider<T> where T : class, new()
|
||||
{
|
||||
public async Task<T> Load()
|
||||
{
|
||||
string fileName = GetTableName(typeof(T));
|
||||
try
|
||||
{
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
string str = (await File.ReadAllTextAsync(fileName).ConfigureAwait(false));
|
||||
return str.DeJson<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Instance.Warning($"{fileName} 配置文件缺失~");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Instance.Error($"{fileName} 配置文件解析有误~ :{ex}");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public async Task<string> LoadString()
|
||||
{
|
||||
string fileName = GetTableName(typeof(T));
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
return (await File.ReadAllTextAsync(fileName).ConfigureAwait(false));
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public async Task Save(T model)
|
||||
{
|
||||
try
|
||||
{
|
||||
string fileName = GetTableName(typeof(T));
|
||||
await File.WriteAllTextAsync(fileName, model.ToJsonIndented(), Encoding.UTF8).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
public async Task Save(string jsonStr)
|
||||
{
|
||||
try
|
||||
{
|
||||
string fileName = GetTableName(typeof(T));
|
||||
await File.WriteAllTextAsync(fileName, jsonStr, Encoding.UTF8).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTableName(Type type)
|
||||
{
|
||||
var attrs = type.GetCustomAttributes(typeof(TableAttribute), false);
|
||||
if (attrs.Length > 0)
|
||||
{
|
||||
return $"{(attrs[0] as TableAttribute).Name}.json";
|
||||
}
|
||||
return $"{type.Name}.json";
|
||||
}
|
||||
}
|
||||
}
|
||||
315
common.libs/extends/NumberExtends.cs
Normal file
315
common.libs/extends/NumberExtends.cs
Normal file
@@ -0,0 +1,315 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace common.libs.extends
|
||||
{
|
||||
public static class NumberExtends
|
||||
{
|
||||
#region 序列化
|
||||
#region double
|
||||
public static byte[] ToBytes(this double num)
|
||||
{
|
||||
return BitConverter.GetBytes(num);
|
||||
}
|
||||
public static unsafe void ToBytes(this double value, Memory<byte> memory)
|
||||
{
|
||||
ref double v = ref value;
|
||||
fixed (void* p = &v)
|
||||
{
|
||||
new Span<byte>(p, 8).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 64
|
||||
public static byte[] ToBytes(this long num)
|
||||
{
|
||||
return BitConverter.GetBytes(num);
|
||||
}
|
||||
public static unsafe void ToBytes(this long value, Memory<byte> memory)
|
||||
{
|
||||
ref long v = ref value;
|
||||
fixed (void* p = &v)
|
||||
{
|
||||
new Span<byte>(p, 8).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
public static unsafe void ToBytes(this long[] value, Memory<byte> memory)
|
||||
{
|
||||
fixed (void* p = &value[0])
|
||||
{
|
||||
new Span<byte>(p, value.Length * sizeof(long)).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
public static byte[] ToBytes(this ulong value)
|
||||
{
|
||||
return BitConverter.GetBytes(value);
|
||||
}
|
||||
public static unsafe void ToBytes(this ulong value, Memory<byte> memory)
|
||||
{
|
||||
ref ulong v = ref value;
|
||||
fixed (void* p = &v)
|
||||
{
|
||||
new Span<byte>(p, sizeof(ulong)).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
public static unsafe void ToBytes(this ulong[] value, Memory<byte> memory)
|
||||
{
|
||||
fixed (void* p = &value[0])
|
||||
{
|
||||
new Span<byte>(p, value.Length * 8).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
public static unsafe void ToBytes(this Memory<ulong> value, Memory<byte> memory)
|
||||
{
|
||||
fixed (void* p = &value.Span[0])
|
||||
{
|
||||
new Span<byte>(p, value.Length * 8).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 32
|
||||
public static byte[] ToBytes(this int num)
|
||||
{
|
||||
return BitConverter.GetBytes(num);
|
||||
}
|
||||
public static unsafe void ToBytes(this int value, Memory<byte> memory)
|
||||
{
|
||||
ref int v = ref value;
|
||||
fixed (void* p = &v)
|
||||
{
|
||||
new Span<byte>(p, 4).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
public static unsafe void ToBytes(this int[] value, Memory<byte> memory)
|
||||
{
|
||||
fixed (void* p = &value[0])
|
||||
{
|
||||
new Span<byte>(p, value.Length * sizeof(int)).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
public static byte[] ToBytes(this uint num)
|
||||
{
|
||||
return BitConverter.GetBytes(num);
|
||||
}
|
||||
public static unsafe void ToBytes(this uint value, Memory<byte> memory)
|
||||
{
|
||||
ref uint v = ref value;
|
||||
fixed (void* p = &v)
|
||||
{
|
||||
new Span<byte>(p, 4).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
public static unsafe void ToBytes(this uint[] value, Memory<byte> memory)
|
||||
{
|
||||
fixed (void* p = &value[0])
|
||||
{
|
||||
new Span<byte>(p, value.Length * sizeof(uint)).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 16
|
||||
public static byte[] ToBytes(this short num)
|
||||
{
|
||||
return BitConverter.GetBytes(num);
|
||||
}
|
||||
public static unsafe void ToBytes(this short value, Memory<byte> memory)
|
||||
{
|
||||
ref short v = ref value;
|
||||
fixed (void* p = &v)
|
||||
{
|
||||
var span = new Span<byte>(p, sizeof(short));
|
||||
memory.Span[0] = span[0];
|
||||
memory.Span[1] = span[1];
|
||||
}
|
||||
}
|
||||
public static unsafe void ToBytes(this short[] value, Memory<byte> memory)
|
||||
{
|
||||
fixed (void* p = &value[0])
|
||||
{
|
||||
new Span<byte>(p, value.Length * sizeof(short)).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(this ushort num)
|
||||
{
|
||||
return BitConverter.GetBytes(num);
|
||||
}
|
||||
public static unsafe void ToBytes(this ushort value, Memory<byte> memory)
|
||||
{
|
||||
ref ushort v = ref value;
|
||||
fixed (void* p = &v)
|
||||
{
|
||||
var span = new Span<byte>(p, sizeof(ushort));
|
||||
memory.Span[0] = span[0];
|
||||
memory.Span[1] = span[1];
|
||||
}
|
||||
}
|
||||
public static unsafe void ToBytes(this ushort[] value, Memory<byte> memory)
|
||||
{
|
||||
fixed (void* p = &value[0])
|
||||
{
|
||||
new Span<byte>(p, value.Length * sizeof(ushort)).CopyTo(memory.Span);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region 反序列化
|
||||
|
||||
#region double
|
||||
public static double ToDouble(this byte[] bytes, int startindex = 0)
|
||||
{
|
||||
return Unsafe.As<byte, double>(ref bytes[startindex]);
|
||||
}
|
||||
public static double ToDouble(this Span<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, double>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static double ToDouble(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, double>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 64
|
||||
public static long ToInt64(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, long>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static long ToInt64(this Span<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, long>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static long ToInt64(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToInt64();
|
||||
}
|
||||
public static long ToInt64(this Memory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToInt64();
|
||||
}
|
||||
public static ulong ToUInt64(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, ulong>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static ulong ToUInt64(this Span<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, ulong>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static ulong ToUInt64(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToUInt64();
|
||||
}
|
||||
public static ulong ToUInt64(this Memory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToUInt64();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 32
|
||||
public static int ToInt32(this byte[] bytes, int startindex = 0)
|
||||
{
|
||||
return Unsafe.As<byte, ushort>(ref bytes[startindex]);
|
||||
}
|
||||
public static int ToInt32(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, int>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static int ToInt32(this Span<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, int>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static int ToInt32(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToInt32();
|
||||
}
|
||||
public static int ToInt32(this Memory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToInt32();
|
||||
}
|
||||
public static uint ToUInt32(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static uint ToUInt32(this Span<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static uint ToUInt32(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToUInt32();
|
||||
}
|
||||
public static uint ToUInt32(this Memory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToUInt32();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 16
|
||||
public static short ToInt16(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, short>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static short ToInt16(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToInt16();
|
||||
}
|
||||
public static short[] ToInt16Array(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
short[] res = new short[span.Length / 2];
|
||||
int index = 0;
|
||||
for (int i = 0; i < res.Length; i++)
|
||||
{
|
||||
res[i] = span.Slice(index, 2).ToInt16();
|
||||
index += 2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public static short[] ToInt16Array(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToInt16Array();
|
||||
}
|
||||
|
||||
public static ushort ToUInt16(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, ushort>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
public static ushort ToUInt16(this Span<byte> span)
|
||||
{
|
||||
return Unsafe.As<byte, ushort>(ref MemoryMarshal.GetReference(span));
|
||||
}
|
||||
|
||||
public static ushort ToUInt16(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToUInt16();
|
||||
}
|
||||
public static ushort ToUInt16(this Memory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToUInt16();
|
||||
}
|
||||
public static ushort[] ToUInt16Array(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
ushort[] res = new ushort[span.Length / 2];
|
||||
int index = 0;
|
||||
for (int i = 0; i < res.Length; i++)
|
||||
{
|
||||
res[i] = span.Slice(index, 2).ToUInt16();
|
||||
index += 2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public static ushort[] ToUInt16Array(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
return memory.Span.ToUInt16Array();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
40
common.libs/extends/SerialzeExtends.cs
Normal file
40
common.libs/extends/SerialzeExtends.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using common.libs.jsonConverters;
|
||||
using System.Text.Json;
|
||||
using System.Text.Unicode;
|
||||
|
||||
namespace common.libs.extends
|
||||
{
|
||||
public static class SerialzeExtends
|
||||
{
|
||||
private static JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions
|
||||
{
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All),
|
||||
AllowTrailingCommas = true,
|
||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = true,
|
||||
Converters = { new IPAddressJsonConverter(), new IPEndpointJsonConverter(), new DateTimeConverter() }
|
||||
};
|
||||
private static JsonSerializerOptions jsonSerializerOptionsIndented = new JsonSerializerOptions
|
||||
{
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All),
|
||||
AllowTrailingCommas = true,
|
||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = true,
|
||||
Converters = { new IPAddressJsonConverter(), new IPEndpointJsonConverter(), new DateTimeConverter() }
|
||||
};
|
||||
public static string ToJson(this object obj)
|
||||
{
|
||||
return JsonSerializer.Serialize(obj, jsonSerializerOptions);
|
||||
}
|
||||
public static string ToJsonIndented(this object obj)
|
||||
{
|
||||
return JsonSerializer.Serialize(obj, jsonSerializerOptionsIndented);
|
||||
}
|
||||
public static T DeJson<T>(this string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(json, options: jsonSerializerOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
77
common.libs/extends/SocketExtends.cs
Normal file
77
common.libs/extends/SocketExtends.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace common.libs.extends
|
||||
{
|
||||
public static class SocketExtends
|
||||
{
|
||||
public static void WindowsUdpBug(this Socket socket)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
try
|
||||
{
|
||||
const uint IOC_IN = 0x80000000;
|
||||
int IOC_VENDOR = 0x18000000;
|
||||
int SIO_UDP_CONNRESET = (int)(IOC_IN | IOC_VENDOR | 12);
|
||||
socket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void IPv6Only(this Socket socket, AddressFamily family, bool val)
|
||||
{
|
||||
if (NetworkHelper.IPv6Support && family == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, val);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void SafeClose(this Socket socket)
|
||||
{
|
||||
if (socket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
//调试注释
|
||||
socket.Disconnect(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
socket.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Reuse(this Socket socket, bool reuse = true)
|
||||
{
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuse);
|
||||
}
|
||||
public static void ReuseBind(this Socket socket, IPEndPoint ip)
|
||||
{
|
||||
socket.Reuse(true);
|
||||
socket.Bind(ip);
|
||||
}
|
||||
|
||||
public static void KeepAlive(this Socket socket, int time = 60, int interval = 5, int retryCount = 5)
|
||||
{
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
|
||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, interval);
|
||||
//socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, retryCount);
|
||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, time);
|
||||
}
|
||||
}
|
||||
}
|
||||
441
common.libs/extends/StringExtends.cs
Normal file
441
common.libs/extends/StringExtends.cs
Normal file
@@ -0,0 +1,441 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Unicode;
|
||||
|
||||
namespace common.libs.extends
|
||||
{
|
||||
/// <summary>
|
||||
/// | Method | Mean | Error | StdDev | Median | Rank | Gen0 | Allocated |
|
||||
/// |--------------------- |-----------:|----------:|-----------:|-----------:|-----:|-------:|----------:|
|
||||
/// utf8
|
||||
/// | TestUTF8OldWrite | 121.167 ns | 0.3973 ns | 1.0467 ns | 120.881 ns | 5 | 0.0687 | 144 B |
|
||||
/// | TestUTF8OldRead | 495.893 ns | 5.7436 ns | 15.4297 ns | 490.850 ns | 8 | 0.9174 | 1920 B |
|
||||
/// | TestUTF8OldWriteRead | 656.620 ns | 6.7862 ns | 18.1138 ns | 650.086 ns | 9 | 0.9861 | 2064 B |
|
||||
/// utf8优化
|
||||
/// | TestUTF8NewWrite | 92.238 ns | 0.3602 ns | 0.9799 ns | 92.008 ns | 4 | - | - |
|
||||
/// | TestUTF8NewRead | 135.711 ns | 0.5058 ns | 1.3412 ns | 136.129 ns | 6 | 0.1338 | 280 B |
|
||||
/// | TestUTF8NewWriteRead | 255.466 ns | 0.5967 ns | 1.6336 ns | 255.028 ns | 7 | 0.1335 | 280 B |
|
||||
/// utf16
|
||||
/// | TestUTF16Write | 7.898 ns | 0.0723 ns | 0.2004 ns | 7.933 ns | 1 | - | - |
|
||||
/// | TestUTF16Read | 19.062 ns | 0.1379 ns | 0.3682 ns | 19.032 ns | 2 | 0.0497 | 104 B |
|
||||
/// | TestUTF16WriteRead | 25.630 ns | 0.1499 ns | 0.4102 ns | 25.524 ns | 3 | 0.0497 | 104 B |
|
||||
/// </summary>
|
||||
public static class StringExtends
|
||||
{
|
||||
/// <summary>
|
||||
/// 截取
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="start"></param>
|
||||
/// <param name="maxLength"></param>
|
||||
/// <returns></returns>
|
||||
public static string SubStr(this string str, int start, int maxLength)
|
||||
{
|
||||
if (maxLength + start > str.Length)
|
||||
{
|
||||
maxLength = str.Length - start;
|
||||
}
|
||||
return str.Substring(start, maxLength);
|
||||
}
|
||||
/// <summary>
|
||||
/// md5
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public static string Md5(this string input)
|
||||
{
|
||||
MD5 md5Hasher = MD5.Create();
|
||||
byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
|
||||
StringBuilder sBuilder = new();
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
sBuilder.Append(data[i].ToString("x2"));
|
||||
}
|
||||
return sBuilder.ToString();
|
||||
}
|
||||
|
||||
|
||||
#region utf8
|
||||
|
||||
/// <summary>
|
||||
/// 慢,但通用,性能基准 1,Allocated 1
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] ToBytes(this string str)
|
||||
{
|
||||
if (str == null) return Helper.EmptyArray;
|
||||
return Encoding.UTF8.GetBytes(str);
|
||||
}
|
||||
/// <summary>
|
||||
/// 慢,但通用,性能基准 1,Allocated 1
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetString(this Span<byte> span)
|
||||
{
|
||||
return Encoding.UTF8.GetString(span);
|
||||
}
|
||||
/// <summary>
|
||||
/// 慢,但通用,性能基准 1,Allocated 1
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetString(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
return Encoding.UTF8.GetString(span);
|
||||
}
|
||||
/// <summary>
|
||||
/// 慢,但通用,性能基准 1,Allocated 1
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetString(this Memory<byte> span)
|
||||
{
|
||||
return Encoding.UTF8.GetString(span.Span);
|
||||
}
|
||||
/// <summary>
|
||||
/// 慢,但通用,性能基准 1,Allocated 1
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetString(this ReadOnlyMemory<byte> span)
|
||||
{
|
||||
return Encoding.UTF8.GetString(span.Span);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region utf8优化
|
||||
/// <summary>
|
||||
/// UTF8比较快,但是中文字符比UTF16更大,Allocated 0.135
|
||||
/// write 0.76 read 0.27 readwrite 0.38
|
||||
/// utf16Length = str.AsSpan().Length
|
||||
/// 保存的时候,保存 utf16Length 和 utf8Length
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="bytes"></param>
|
||||
/// <returns></returns>
|
||||
public static int ToUTF8Bytes(this ReadOnlySpan<char> str, Memory<byte> bytes)
|
||||
{
|
||||
if (str.Length == 0) return 0;
|
||||
Utf8.FromUtf16(str, bytes.Span, out var _, out var utf8Length, replaceInvalidSequences: false);
|
||||
return utf8Length;
|
||||
}
|
||||
/// <summary>
|
||||
/// UTF8比较快,但是中文字符比UTF16更大,Allocated 0.135
|
||||
/// write 0.76 read 0.27 readwrite 0.38
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static Memory<byte> ToUTF8Bytes(this string str)
|
||||
{
|
||||
int utf16Length = 0, utf8Length = 0;
|
||||
byte[] bytes;
|
||||
int length;
|
||||
if (str != null)
|
||||
{
|
||||
var source = str.AsSpan();
|
||||
utf16Length = source.Length;
|
||||
bytes = new byte[(source.Length + 1) * 3 + 8];
|
||||
Utf8.FromUtf16(str, bytes.AsSpan(8), out _, out utf8Length, replaceInvalidSequences: false);
|
||||
length = utf8Length + 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes = new byte[8];
|
||||
length = 8;
|
||||
}
|
||||
utf16Length.ToBytes(bytes);
|
||||
utf8Length.ToBytes(bytes.AsMemory(4));
|
||||
|
||||
return bytes.AsMemory(0, length);
|
||||
}
|
||||
/// <summary>
|
||||
/// UTF8比较快,但是中文字符比UTF16更大,Allocated 0.135
|
||||
/// write 0.76 read 0.27 readwrite 0.38
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <param name="utf16Length"></param>
|
||||
/// <param name="utf8Length"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF8String(this Span<byte> span, int utf16Length, int utf8Length)
|
||||
{
|
||||
if (span.Length == 0) return string.Empty;
|
||||
return ReadUtf8(span, utf16Length, utf8Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// UTF8比较快,但是中文字符比UTF16更大,Allocated 0.135
|
||||
/// write 0.76 read 0.27 readwrite 0.38
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF8String(this Span<byte> span)
|
||||
{
|
||||
if (span.Length == 0) return string.Empty;
|
||||
int utf16Length = span.ToInt32();
|
||||
int utf8Length = span.Slice(4).ToInt32();
|
||||
return ReadUtf8(span.Slice(8), utf16Length, utf8Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// UTF8比较快,但是中文字符比UTF16更大,Allocated 0.135
|
||||
/// write 0.76 read 0.27 readwrite 0.38
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <param name="utf16Length"></param>
|
||||
/// <param name="utf8Length"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF8String(this ReadOnlySpan<byte> span, int utf16Length, int utf8Length)
|
||||
{
|
||||
if (span.Length == 0) return string.Empty;
|
||||
return ReadUtf8(span, utf16Length, utf8Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// UTF8比较快,但是中文字符比UTF16更大,Allocated 0.135
|
||||
/// write 0.76 read 0.27 readwrite 0.38
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF8String(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
if (span.Length == 0) return string.Empty;
|
||||
int utf16Length = span.ToInt32();
|
||||
int utf8Length = span.Slice(4).ToInt32();
|
||||
return ReadUtf8(span.Slice(8), utf16Length, utf8Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// UTF8比较快,但是中文字符比UTF16更大,Allocated 0.135
|
||||
/// write 0.76 read 0.27 readwrite 0.38
|
||||
/// </summary>
|
||||
/// <param name="memory"></param>
|
||||
/// <param name="utf16Length"></param>
|
||||
/// <param name="utf8Length"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF8String(this Memory<byte> memory, int utf16Length, int utf8Length)
|
||||
{
|
||||
if (memory.Length == 0) return string.Empty;
|
||||
return ReadUtf8(memory.Span, utf16Length, utf8Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// UTF8比较快,但是中文字符比UTF16更大,Allocated 0.135
|
||||
/// write 0.76 read 0.27 readwrite 0.38
|
||||
/// </summary>
|
||||
/// <param name="memory"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF8String(this Memory<byte> memory)
|
||||
{
|
||||
if (memory.Length == 0) return string.Empty;
|
||||
int utf16Length = memory.ToInt32();
|
||||
int utf8Length = memory.Slice(4).ToInt32();
|
||||
return ReadUtf8(memory.Slice(8).Span, utf16Length, utf8Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// UTF8比较快,但是中文字符比UTF16更大,Allocated 0.135
|
||||
/// write 0.76 read 0.27 readwrite 0.38
|
||||
/// </summary>
|
||||
/// <param name="memory"></param>
|
||||
/// <param name="utf16Length"></param>
|
||||
/// <param name="utf8Length"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF8String(this ReadOnlyMemory<byte> memory, int utf16Length, int utf8Length)
|
||||
{
|
||||
if (memory.Length == 0)
|
||||
return string.Empty;
|
||||
return ReadUtf8(memory.Span, utf16Length, utf8Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// UTF8比较快,但是中文字符比UTF16更大,Allocated 0.135
|
||||
/// write 0.76 read 0.27 readwrite 0.38
|
||||
/// </summary>
|
||||
/// <param name="memory"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF8String(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
if (memory.Length == 0)
|
||||
return string.Empty;
|
||||
int utf16Length = memory.ToInt32();
|
||||
int utf8Length = memory.Slice(4).ToInt32();
|
||||
return ReadUtf8(memory.Slice(8).Span, utf16Length, utf8Length);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region utf16
|
||||
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="bytes"></param>
|
||||
public static ReadOnlySpan<byte> GetUTF16Bytes(this string str)
|
||||
{
|
||||
if (str == null) return Helper.EmptyArray;
|
||||
return MemoryMarshal.AsBytes(str.AsSpan());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] ToUTF16Bytes(this string str)
|
||||
{
|
||||
byte[] bytes;
|
||||
|
||||
if (str != null)
|
||||
{
|
||||
var source = MemoryMarshal.AsBytes(str.AsSpan());
|
||||
bytes = new byte[source.Length + 4];
|
||||
source.CopyTo(bytes.AsSpan(4));
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes = new byte[4];
|
||||
}
|
||||
str.Length.ToBytes(bytes);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <param name="strLength"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF16String(this Span<byte> span, int strLength)
|
||||
{
|
||||
if (span.Length == 0) return string.Empty;
|
||||
return ReadUtf16(span, strLength);
|
||||
}
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <param name="strLength"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF16String(this Span<byte> span)
|
||||
{
|
||||
if (span.Length == 0) return string.Empty;
|
||||
int strLength = span.ToInt32();
|
||||
return ReadUtf16(span.Slice(4), strLength);
|
||||
}
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <param name="strLength"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF16String(this ReadOnlySpan<byte> span, int strLength)
|
||||
{
|
||||
if (span.Length == 0) return string.Empty;
|
||||
return ReadUtf16(span, strLength);
|
||||
}
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <param name="strLength"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF16String(this ReadOnlySpan<byte> span)
|
||||
{
|
||||
if (span.Length == 0)
|
||||
return string.Empty;
|
||||
int strLength = span.ToInt32();
|
||||
return ReadUtf16(span.Slice(4), strLength);
|
||||
}
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="memory"></param>
|
||||
/// <param name="strLength"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF16String(this Memory<byte> memory, int strLength)
|
||||
{
|
||||
if (memory.Length == 0)
|
||||
return string.Empty;
|
||||
return ReadUtf16(memory.Span, strLength);
|
||||
}
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="memory"></param>
|
||||
/// <param name="strLength"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF16String(this Memory<byte> memory)
|
||||
{
|
||||
if (memory.Length == 0) return string.Empty;
|
||||
return memory.Span.GetUTF16String();
|
||||
}
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="memory"></param>
|
||||
/// <param name="strLength"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF16String(this ReadOnlyMemory<byte> memory, int strLength)
|
||||
{
|
||||
if (memory.Length == 0)
|
||||
return string.Empty;
|
||||
return ReadUtf16(memory.Span, strLength);
|
||||
}
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="memory"></param>
|
||||
/// <param name="strLength"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF16String(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
if (memory.Length == 0) return string.Empty;
|
||||
return memory.Span.GetUTF16String();
|
||||
}
|
||||
/// <summary>
|
||||
/// utf16非常快,但是,ASCII 字符的大小将是原来的两倍,中文字符则比UTF8略小,Allocated 0.05
|
||||
/// write 0.065 read 0.038 readwrite 0.039
|
||||
/// </summary>
|
||||
/// <param name="memory"></param>
|
||||
/// <param name="strLength"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUTF16String(this byte[] memory, int strLength)
|
||||
{
|
||||
if (memory.Length == 0) return string.Empty;
|
||||
return ReadUtf16(memory, strLength);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
static string ReadUtf8(ReadOnlySpan<byte> span, int utf16Length, int utf8Length)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* p = &span[0])
|
||||
{
|
||||
return string.Create(utf16Length, ((IntPtr)p, utf8Length), static (dest, state) =>
|
||||
{
|
||||
var src = MemoryMarshal.CreateSpan(ref Unsafe.AsRef<byte>((byte*)state.Item1), state.Item2);
|
||||
Utf8.ToUtf16(src, dest, out var bytesRead, out var charsWritten, replaceInvalidSequences: false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
static string ReadUtf16(ReadOnlySpan<byte> span, int strLength)
|
||||
{
|
||||
ReadOnlySpan<char> src = MemoryMarshal.Cast<byte, char>(span).Slice(0, strLength);
|
||||
return new string(src);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
19
common.libs/jsonConverters/DateTimeConverter.cs
Normal file
19
common.libs/jsonConverters/DateTimeConverter.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace common.libs.jsonConverters
|
||||
{
|
||||
public sealed class DateTimeConverter : JsonConverter<DateTime>
|
||||
{
|
||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return DateTime.Parse(s: reader.GetString());
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
}
|
||||
}
|
||||
19
common.libs/jsonConverters/IPAddressJsonConverter.cs
Normal file
19
common.libs/jsonConverters/IPAddressJsonConverter.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace common.libs.jsonConverters
|
||||
{
|
||||
public sealed class IPAddressJsonConverter : JsonConverter<IPAddress>
|
||||
{
|
||||
public override IPAddress Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return IPAddress.Parse(reader.GetString());
|
||||
}
|
||||
public override void Write(Utf8JsonWriter writer, IPAddress value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
19
common.libs/jsonConverters/IPEndpointJsonConverter.cs
Normal file
19
common.libs/jsonConverters/IPEndpointJsonConverter.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace common.libs.jsonConverters
|
||||
{
|
||||
public sealed class IPEndpointJsonConverter : JsonConverter<IPEndPoint>
|
||||
{
|
||||
public override IPEndPoint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return IPEndPoint.Parse(reader.GetString());
|
||||
}
|
||||
public override void Write(Utf8JsonWriter writer, IPEndPoint value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
140
common.libs/socks5/Socks5Enum.cs
Normal file
140
common.libs/socks5/Socks5Enum.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
|
||||
namespace common.libs.socks5
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据验证结果
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum EnumProxyValidateDataResult : byte
|
||||
{
|
||||
Equal = 1,
|
||||
TooShort = 2,
|
||||
TooLong = 4,
|
||||
Bad = 8,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前处于socks5协议的哪一步
|
||||
/// </summary>
|
||||
public enum Socks5EnumStep : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 第一次请求,处理认证方式
|
||||
/// </summary>
|
||||
Request = 1,
|
||||
/// <summary>
|
||||
/// 如果有认证
|
||||
/// </summary>
|
||||
Auth = 2,
|
||||
/// <summary>
|
||||
/// 发送命令,CONNECT BIND 还是 UDP ASSOCIATE
|
||||
/// </summary>
|
||||
Command = 3,
|
||||
/// <summary>
|
||||
/// 转发
|
||||
/// </summary>
|
||||
Forward = 4,
|
||||
/// <summary>
|
||||
/// udp转发
|
||||
/// </summary>
|
||||
ForwardUdp = 5,
|
||||
|
||||
None = 0
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// socks5的连接地址类型
|
||||
/// </summary>
|
||||
public enum Socks5EnumAddressType : byte
|
||||
{
|
||||
IPV4 = 1,
|
||||
Domain = 3,
|
||||
IPV6 = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// socks5的认证类型
|
||||
/// </summary>
|
||||
public enum Socks5EnumAuthType : byte
|
||||
{
|
||||
NoAuth = 0x00,
|
||||
GSSAPI = 0x01,
|
||||
Password = 0x02,
|
||||
IANA = 0x03,
|
||||
UnKnow = 0x80,
|
||||
NotSupported = 0xff,
|
||||
}
|
||||
/// <summary>
|
||||
/// socks5的认证状态0成功 其它失败
|
||||
/// </summary>
|
||||
public enum Socks5EnumAuthState : byte
|
||||
{
|
||||
Success = 0x00,
|
||||
UnKnow = 0xff,
|
||||
}
|
||||
/// <summary>
|
||||
/// socks5的请求指令
|
||||
/// </summary>
|
||||
public enum Socks5EnumRequestCommand : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 连接上游服务器
|
||||
/// </summary>
|
||||
Connect = 1,
|
||||
/// <summary>
|
||||
/// 绑定,客户端会接收来自代理服务器的链接,著名的FTP被动模式
|
||||
/// </summary>
|
||||
Bind = 2,
|
||||
/// <summary>
|
||||
/// UDP中继
|
||||
/// </summary>
|
||||
UdpAssociate = 3
|
||||
}
|
||||
/// <summary>
|
||||
/// socks5的请求的回复数据的指令
|
||||
/// </summary>
|
||||
public enum Socks5EnumResponseCommand : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 代理服务器连接目标服务器成功
|
||||
/// </summary>
|
||||
ConnecSuccess = 0,
|
||||
/// <summary>
|
||||
/// 代理服务器故障
|
||||
/// </summary>
|
||||
ServerError = 1,
|
||||
/// <summary>
|
||||
/// 代理服务器规则集不允许连接
|
||||
/// </summary>
|
||||
ConnectNotAllow = 2,
|
||||
/// <summary>
|
||||
/// 网络无法访问
|
||||
/// </summary>
|
||||
NetworkError = 3,
|
||||
/// <summary>
|
||||
/// 目标服务器无法访问(主机名无效)
|
||||
/// </summary>
|
||||
ConnectFail = 4,
|
||||
/// <summary>
|
||||
/// 连接目标服务器被拒绝
|
||||
/// </summary>
|
||||
DistReject = 5,
|
||||
/// <summary>
|
||||
/// TTL已过期
|
||||
/// </summary>
|
||||
TTLTimeout = 6,
|
||||
/// <summary>
|
||||
/// 不支持的命令
|
||||
/// </summary>
|
||||
CommandNotAllow = 7,
|
||||
/// <summary>
|
||||
/// 不支持的目标服务器地址类型
|
||||
/// </summary>
|
||||
AddressNotAllow = 8,
|
||||
/// <summary>
|
||||
/// 未分配
|
||||
/// </summary>
|
||||
Unknow = 8,
|
||||
}
|
||||
}
|
||||
322
common.libs/socks5/Socks5Parser.cs
Normal file
322
common.libs/socks5/Socks5Parser.cs
Normal file
@@ -0,0 +1,322 @@
|
||||
using common.libs.extends;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace common.libs.socks5
|
||||
{
|
||||
/// <summary>
|
||||
/// socks5 数据包解析和组装
|
||||
/// </summary>
|
||||
public sealed class Socks5Parser
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 获取客户端过来的支持的认证方式列表
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <returns></returns>
|
||||
public static Socks5EnumAuthType[] GetAuthMethods(ReadOnlySpan<byte> span)
|
||||
{
|
||||
//VER NMETHODS METHODS
|
||||
// 1 1 1-255
|
||||
//版本 支持哪些认证 一个认证方式一个字节
|
||||
byte length = span[1];
|
||||
Socks5EnumAuthType[] res = new Socks5EnumAuthType[length];
|
||||
for (byte i = 0; i < length; i++)
|
||||
{
|
||||
res[i] = (Socks5EnumAuthType)span[2 + i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取账号密码
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <returns></returns>
|
||||
public static (string username, string password) GetPasswordAuthInfo(Span<byte> span)
|
||||
{
|
||||
/*
|
||||
子版本 username长度 username password长度 password
|
||||
0x01
|
||||
*/
|
||||
string username = span.Slice(2, span[1]).GetString();
|
||||
string password = span.Slice(2 + span[1] + 1, span[2 + span[1]]).GetString();
|
||||
return (username, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取地址
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static Memory<byte> GetRemoteEndPoint(Memory<byte> data, out Socks5EnumAddressType addressType, out ushort port, out int index)
|
||||
{
|
||||
//VERSION COMMAND RSV ATYPE DST.ADDR DST.PORT
|
||||
//去掉 VERSION COMMAND RSV
|
||||
Memory<byte> memory = data.Slice(3);
|
||||
Span<byte> span = memory.Span;
|
||||
addressType = (Socks5EnumAddressType)span[0];
|
||||
index = 0;
|
||||
Memory<byte> result = Helper.EmptyArray;
|
||||
|
||||
switch (addressType)
|
||||
{
|
||||
case Socks5EnumAddressType.IPV4:
|
||||
{
|
||||
result = memory.Slice(1, 4);
|
||||
index = 1 + 4;
|
||||
}
|
||||
break;
|
||||
case Socks5EnumAddressType.Domain:
|
||||
{
|
||||
result = memory.Slice(2, span[1]);
|
||||
index = 2 + span[1];
|
||||
}
|
||||
break;
|
||||
case Socks5EnumAddressType.IPV6:
|
||||
{
|
||||
result = memory.Slice(1, 16);
|
||||
index = 1 + 16;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
port = BinaryPrimitives.ReadUInt16BigEndian(span.Slice(index, 2));
|
||||
index += 2;
|
||||
index += 3;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取udp中继中的数据
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public static Memory<byte> GetUdpData(Memory<byte> span)
|
||||
{
|
||||
//RSV FRAG ATYPE DST.ADDR DST.PORT DATA
|
||||
//去掉 RSV FRAG RSV占俩字节
|
||||
span = span.Slice(3);
|
||||
return (Socks5EnumAddressType)span.Span[0] switch
|
||||
{
|
||||
Socks5EnumAddressType.IPV4 => span[(1 + 4 + 2)..],
|
||||
Socks5EnumAddressType.IPV6 => span[(1 + 16 + 2)..],
|
||||
Socks5EnumAddressType.Domain => span[(2 + span.Span[1] + 2)..],
|
||||
_ => Helper.EmptyArray,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成connect返回包
|
||||
/// </summary>
|
||||
/// <param name="remoteEndPoint"></param>
|
||||
/// <param name="responseCommand"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe byte[] MakeConnectResponse(IPEndPoint remoteEndPoint, byte responseCommand)
|
||||
{
|
||||
//VER REP RSV ATYPE BND.ADDR BND.PORT
|
||||
|
||||
byte[] res = new byte[6 + (remoteEndPoint.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? 4 : 16)];
|
||||
var span = res.AsSpan();
|
||||
|
||||
res[0] = 5;
|
||||
res[1] = responseCommand;
|
||||
res[2] = 0;
|
||||
res[3] = (byte)(remoteEndPoint.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? Socks5EnumAddressType.IPV4 : Socks5EnumAddressType.IPV6);
|
||||
|
||||
remoteEndPoint.Address.TryWriteBytes(span.Slice(4), out _);
|
||||
|
||||
int port = remoteEndPoint.Port;
|
||||
ref int _port = ref port;
|
||||
fixed (void* p = &_port)
|
||||
{
|
||||
byte* pp = (byte*)p;
|
||||
res[^2] = *(pp + 1);
|
||||
res[^1] = *pp;
|
||||
}
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
/// <summary>
|
||||
/// 生成udp中中继数据包
|
||||
/// </summary>
|
||||
/// <param name="remoteEndPoint"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe byte[] MakeUdpResponse(IPEndPoint remoteEndPoint, Memory<byte> data, out int length)
|
||||
{
|
||||
//RSV FRAG ATYPE DST.ADDR DST.PORT DATA
|
||||
//RSV占俩字节
|
||||
|
||||
int ipLength = (remoteEndPoint.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? 4 : 16);
|
||||
length = 4 + ipLength + 2 + data.Length;
|
||||
|
||||
byte[] res = ArrayPool<byte>.Shared.Rent(length);
|
||||
var span = res.AsSpan();
|
||||
|
||||
res[0] = 0;
|
||||
res[1] = 0;
|
||||
res[2] = 0; //FRAG
|
||||
res[3] = (byte)(ipLength == 4 ? Socks5EnumAddressType.IPV4 : Socks5EnumAddressType.IPV6);
|
||||
|
||||
int index = 4;
|
||||
|
||||
remoteEndPoint.Address.TryWriteBytes(span.Slice(index), out _);
|
||||
index += ipLength;
|
||||
|
||||
int port = remoteEndPoint.Port;
|
||||
ref int _port = ref port;
|
||||
fixed (void* p = &_port)
|
||||
{
|
||||
byte* pp = (byte*)p;
|
||||
res[index] = *(pp + 1);
|
||||
res[index + 1] = *pp;
|
||||
}
|
||||
index += 2;
|
||||
|
||||
data.CopyTo(res.AsMemory(index, data.Length));
|
||||
|
||||
return res;
|
||||
}
|
||||
public static void Return(byte[] data)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(data);
|
||||
}
|
||||
|
||||
|
||||
public static EnumProxyValidateDataResult ValidateData(Socks5EnumStep step, Memory<byte> data)
|
||||
{
|
||||
return step switch
|
||||
{
|
||||
Socks5EnumStep.Request => ValidateRequestData(data),
|
||||
Socks5EnumStep.Command => ValidateCommandData(data),
|
||||
Socks5EnumStep.Auth => ValidateAuthData(data, Socks5EnumAuthType.Password),
|
||||
Socks5EnumStep.Forward => EnumProxyValidateDataResult.Equal,
|
||||
Socks5EnumStep.ForwardUdp => EnumProxyValidateDataResult.Equal,
|
||||
_ => EnumProxyValidateDataResult.Equal
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
/// 验证 request数据完整性
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static EnumProxyValidateDataResult ValidateRequestData(Memory<byte> data)
|
||||
{
|
||||
/*
|
||||
* VERSION METHODS_COUNT METHODS
|
||||
1字节 1字节 1到255字节,长度由METHODS_COUNT值决定
|
||||
0x05 0x03 0x00 0x01 0x02
|
||||
*/
|
||||
|
||||
|
||||
if (data.Length < 2 || data.Length < 2 + data.Span[1])
|
||||
{
|
||||
return EnumProxyValidateDataResult.TooShort;
|
||||
}
|
||||
/*
|
||||
if (data.Length > 2 + data.Span[1])
|
||||
{
|
||||
return EnumProxyValidateDataResult.TooLong;
|
||||
}*/
|
||||
|
||||
return EnumProxyValidateDataResult.Equal;
|
||||
}
|
||||
/// <summary>
|
||||
/// 验证command数据完整性
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static EnumProxyValidateDataResult ValidateCommandData(Memory<byte> data)
|
||||
{
|
||||
/*
|
||||
* VERSION COMMAND RSV ADDRESS_TYPE DST.ADDR DST.PORT
|
||||
* 1 1 1 1 1-255 2
|
||||
* 域名模式下 DST.ADDR第一个字节是域名长度,那么整个数据至少8个字节
|
||||
*/
|
||||
if (data.Length < 8) return EnumProxyValidateDataResult.TooShort;
|
||||
|
||||
var span = data.Span;
|
||||
int addrLength = (Socks5EnumAddressType)span[3] switch
|
||||
{
|
||||
Socks5EnumAddressType.IPV4 => 4 + 2,
|
||||
Socks5EnumAddressType.Domain => span[4] + 1 + 2, //DST.ADDR第一个字节是域名长度 剩下的才是域名数据
|
||||
Socks5EnumAddressType.IPV6 => 16 + 2,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
if (data.Length < 4 + addrLength)
|
||||
{
|
||||
return EnumProxyValidateDataResult.TooShort;
|
||||
}
|
||||
/*
|
||||
if (data.Length > 4 + addrLength)
|
||||
{
|
||||
return EnumProxyValidateDataResult.TooLong;
|
||||
}
|
||||
*/
|
||||
return EnumProxyValidateDataResult.Equal;
|
||||
}
|
||||
/// <summary>
|
||||
/// 验证认证数据完整性
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="authType"></param>
|
||||
/// <returns></returns>
|
||||
public static EnumProxyValidateDataResult ValidateAuthData(Memory<byte> data, Socks5EnumAuthType authType)
|
||||
{
|
||||
return authType switch
|
||||
{
|
||||
Socks5EnumAuthType.NoAuth => EnumProxyValidateDataResult.Equal,
|
||||
Socks5EnumAuthType.Password => ValidateAuthPasswordData(data),
|
||||
Socks5EnumAuthType.GSSAPI => EnumProxyValidateDataResult.Equal,
|
||||
Socks5EnumAuthType.IANA => EnumProxyValidateDataResult.Equal,
|
||||
Socks5EnumAuthType.UnKnow => EnumProxyValidateDataResult.Bad,
|
||||
Socks5EnumAuthType.NotSupported => EnumProxyValidateDataResult.Bad,
|
||||
_ => EnumProxyValidateDataResult.Bad,
|
||||
};
|
||||
}
|
||||
private static EnumProxyValidateDataResult ValidateAuthPasswordData(Memory<byte> data)
|
||||
{
|
||||
/*
|
||||
VERSION USERNAME_LENGTH USERNAME PASSWORD_LENGTH PASSWORD
|
||||
1字节 1字节 1到255字节 1字节 1到255字节
|
||||
0x01 0x01 0x0a 0x01 0x0a
|
||||
*/
|
||||
|
||||
var span = data.Slice(1).Span;
|
||||
//至少有 USERNAME_LENGTH PASSWORD_LENGTH 字节以上
|
||||
if (span.Length <= 2)
|
||||
{
|
||||
return EnumProxyValidateDataResult.TooShort;
|
||||
}
|
||||
|
||||
byte nameLength = span[0];
|
||||
//至少有 USERNAME_LENGTH USERNAME PASSWORD_LENGTH
|
||||
if (span.Length < nameLength + 1 + 1)
|
||||
{
|
||||
return EnumProxyValidateDataResult.TooShort;
|
||||
}
|
||||
|
||||
byte passwordLength = span[1 + nameLength];
|
||||
if (span.Length < 1 + 1 + nameLength + passwordLength)
|
||||
{
|
||||
return EnumProxyValidateDataResult.TooShort;
|
||||
}
|
||||
/*
|
||||
if (span.Length > 1 + 1 + nameLength + passwordLength)
|
||||
{
|
||||
return EnumProxyValidateDataResult.TooLong;
|
||||
}
|
||||
*/
|
||||
return EnumProxyValidateDataResult.Equal;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user