mirror of
https://github.com/netchx/netch.git
synced 2026-03-14 17:43:18 +08:00
Update ServerHelper.cs
Extract and Refactor DelayTestHelper
This commit is contained in:
@@ -78,7 +78,7 @@ namespace Netch.Forms
|
|||||||
|
|
||||||
LoadServers();
|
LoadServers();
|
||||||
SelectLastServer();
|
SelectLastServer();
|
||||||
ServerHelper.DelayTestHelper.UpdateInterval();
|
DelayTestHelper.UpdateTick(true);
|
||||||
|
|
||||||
ModeHelper.InitWatcher();
|
ModeHelper.InitWatcher();
|
||||||
ModeHelper.Load();
|
ModeHelper.Load();
|
||||||
@@ -592,7 +592,7 @@ namespace Netch.Forms
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (oldSettings.DetectionTick != Global.Settings.DetectionTick)
|
if (oldSettings.DetectionTick != Global.Settings.DetectionTick)
|
||||||
ServerHelper.DelayTestHelper.UpdateInterval();
|
DelayTestHelper.UpdateTick(true);
|
||||||
|
|
||||||
if (oldSettings.ProfileCount != Global.Settings.ProfileCount)
|
if (oldSettings.ProfileCount != Global.Settings.ProfileCount)
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
@@ -663,7 +663,7 @@ namespace Netch.Forms
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await ServerHelper.DelayTestHelper.TestAllDelayAsync();
|
await DelayTestHelper.PerformTestAsync(true);
|
||||||
Enable();
|
Enable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -995,7 +995,7 @@ namespace Netch.Forms
|
|||||||
|
|
||||||
_state = value;
|
_state = value;
|
||||||
|
|
||||||
ServerHelper.DelayTestHelper.Enabled = IsWaiting(_state);
|
DelayTestHelper.Enabled = IsWaiting(_state);
|
||||||
|
|
||||||
StatusText();
|
StatusText();
|
||||||
switch (value)
|
switch (value)
|
||||||
@@ -1057,14 +1057,11 @@ namespace Netch.Forms
|
|||||||
State = State.Stopped;
|
State = State.Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsWaiting()
|
private bool IsWaiting() => IsWaiting(_state);
|
||||||
{
|
|
||||||
return State == State.Waiting || State == State.Stopped;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsWaiting(State state)
|
private static bool IsWaiting(State state)
|
||||||
{
|
{
|
||||||
return state == State.Waiting || state == State.Stopped;
|
return state is State.Waiting or State.Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1253,7 +1250,7 @@ namespace Netch.Forms
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
State = State.Terminating;
|
// State = State.Terminating;
|
||||||
NotifyIcon.Visible = false;
|
NotifyIcon.Visible = false;
|
||||||
Hide();
|
Hide();
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace Netch.Forms
|
|||||||
|
|
||||||
BindTextBox<int>(ProfileCountTextBox, i => i > -1, i => Global.Settings.ProfileCount = i, Global.Settings.ProfileCount);
|
BindTextBox<int>(ProfileCountTextBox, i => i > -1, i => Global.Settings.ProfileCount = i, Global.Settings.ProfileCount);
|
||||||
BindTextBox<int>(DetectionTickTextBox,
|
BindTextBox<int>(DetectionTickTextBox,
|
||||||
i => ServerHelper.DelayTestHelper.Range.InRange(i),
|
i => DelayTestHelper.Range.InRange(i),
|
||||||
i => Global.Settings.DetectionTick = i,
|
i => Global.Settings.DetectionTick = i,
|
||||||
Global.Settings.DetectionTick);
|
Global.Settings.DetectionTick);
|
||||||
|
|
||||||
|
|||||||
@@ -43,11 +43,7 @@ namespace Netch.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 代理类型
|
/// 代理类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual string Type { get; } = string.Empty;
|
public abstract string Type { get; }
|
||||||
|
|
||||||
[JsonExtensionData]
|
|
||||||
// ReSharper disable once CollectionNeverUpdated.Global
|
|
||||||
public Dictionary<string, object> ExtensionData { get; set; } = new();
|
|
||||||
|
|
||||||
public object Clone()
|
public object Clone()
|
||||||
{
|
{
|
||||||
@@ -62,7 +58,7 @@ namespace Netch.Models
|
|||||||
{
|
{
|
||||||
var remark = string.IsNullOrWhiteSpace(Remark) ? $"{Hostname}:{Port}" : Remark;
|
var remark = string.IsNullOrWhiteSpace(Remark) ? $"{Hostname}:{Port}" : Remark;
|
||||||
|
|
||||||
var shortName = Type.IsNullOrEmpty() ? "WTF" : ServerHelper.GetUtilByTypeName(Type).ShortName;
|
var shortName = ServerHelper.GetUtilByTypeName(Type).ShortName;
|
||||||
|
|
||||||
return $"[{shortName}][{Group}] {remark}";
|
return $"[{shortName}][{Group}] {remark}";
|
||||||
}
|
}
|
||||||
|
|||||||
107
Netch/Utils/DelayTestHelper.cs
Normal file
107
Netch/Utils/DelayTestHelper.cs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.VisualStudio.Threading;
|
||||||
|
using Netch.Models;
|
||||||
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
|
namespace Netch.Utils
|
||||||
|
{
|
||||||
|
public static class DelayTestHelper
|
||||||
|
{
|
||||||
|
private static readonly Timer Timer;
|
||||||
|
|
||||||
|
private static readonly SemaphoreSlim Lock = new(1, 1);
|
||||||
|
|
||||||
|
private static readonly SemaphoreSlim PoolLock = new(16, 16);
|
||||||
|
|
||||||
|
public static readonly NumberRange Range = new(0, int.MaxValue / 1000);
|
||||||
|
|
||||||
|
private static bool _enabled = true;
|
||||||
|
|
||||||
|
static DelayTestHelper()
|
||||||
|
{
|
||||||
|
Timer = new Timer
|
||||||
|
{
|
||||||
|
Interval = 10000,
|
||||||
|
AutoReset = true
|
||||||
|
};
|
||||||
|
|
||||||
|
Timer.Elapsed += (_, _) => PerformTestAsync().Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Enabled
|
||||||
|
{
|
||||||
|
get => _enabled;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_enabled = value;
|
||||||
|
UpdateTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="waitFinish">if does not get lock, block until last release</param>
|
||||||
|
public static async Task PerformTestAsync(bool waitFinish = false)
|
||||||
|
{
|
||||||
|
if (Lock.CurrentCount == 0)
|
||||||
|
{
|
||||||
|
if (waitFinish)
|
||||||
|
{
|
||||||
|
await Lock.WaitAsync();
|
||||||
|
Lock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Lock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tasks = Global.Settings.Server.Select(async s =>
|
||||||
|
{
|
||||||
|
await PoolLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await s.PingAsync();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
PoolLock.Release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Lock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateTick(bool performTestAtOnce = false)
|
||||||
|
{
|
||||||
|
UpdateTick(Global.Settings.DetectionTick, performTestAtOnce);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="interval">interval(seconds), 0 disable, MaxValue <c>int.MaxValue/1000</c></param>
|
||||||
|
/// <param name="performTestAtOnce"></param>
|
||||||
|
private static void UpdateTick(int interval, bool performTestAtOnce = false)
|
||||||
|
{
|
||||||
|
Timer.Stop();
|
||||||
|
|
||||||
|
var enable = Enabled && interval > 0 && Range.InRange(interval);
|
||||||
|
if (enable)
|
||||||
|
{
|
||||||
|
Timer.Interval = interval * 1000;
|
||||||
|
Timer.Start();
|
||||||
|
if (performTestAtOnce)
|
||||||
|
PerformTestAsync().Forget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,19 +11,9 @@ namespace Netch.Utils
|
|||||||
|
|
||||||
public override Server Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override Server Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
var jsonElement = JsonSerializer.Deserialize<JsonElement>(ref reader)!;
|
var jsonElement = JsonSerializer.Deserialize<JsonElement>(ref reader);
|
||||||
|
var type = ServerHelper.GetTypeByTypeName(jsonElement.GetProperty("Type").GetString()!);
|
||||||
try
|
return (Server)JsonSerializer.Deserialize(jsonElement.GetRawText(), type)!;
|
||||||
{
|
|
||||||
var type = ServerHelper.GetTypeByTypeName(jsonElement.GetProperty("Type").GetString()!);
|
|
||||||
// TODO replace with .NET 6 Deserialize from DOM
|
|
||||||
return (Server)JsonSerializer.Deserialize(jsonElement.GetRawText(), type)!;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Unsupported Server Type
|
|
||||||
return JsonSerializer.Deserialize<Server>(jsonElement.GetRawText(), new JsonSerializerOptions())!;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, Server value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, Server value, JsonSerializerOptions options)
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.VisualStudio.Threading;
|
|
||||||
using Netch.Interfaces;
|
using Netch.Interfaces;
|
||||||
using Netch.Models;
|
|
||||||
using Timer = System.Timers.Timer;
|
|
||||||
|
|
||||||
namespace Netch.Utils
|
namespace Netch.Utils
|
||||||
{
|
{
|
||||||
@@ -23,102 +17,11 @@ namespace Netch.Utils
|
|||||||
ServerUtilDictionary = serversUtilsTypes.Select(t => (IServerUtil)Activator.CreateInstance(t)!).ToDictionary(util => util.TypeName);
|
ServerUtilDictionary = serversUtilsTypes.Select(t => (IServerUtil)Activator.CreateInstance(t)!).ToDictionary(util => util.TypeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Delay
|
public static Dictionary<string, IServerUtil> ServerUtilDictionary { get; }
|
||||||
|
|
||||||
public static class DelayTestHelper
|
|
||||||
{
|
|
||||||
private static readonly Timer Timer;
|
|
||||||
private static readonly object TestAllLock = new();
|
|
||||||
|
|
||||||
private static readonly SemaphoreSlim SemaphoreSlim = new(1, 16);
|
|
||||||
|
|
||||||
public static readonly NumberRange Range = new(0, int.MaxValue / 1000);
|
|
||||||
|
|
||||||
static DelayTestHelper()
|
|
||||||
{
|
|
||||||
Timer = new Timer
|
|
||||||
{
|
|
||||||
Interval = 10000,
|
|
||||||
AutoReset = true
|
|
||||||
};
|
|
||||||
|
|
||||||
Timer.Elapsed += (_, _) => TestAllDelayAsync().Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Enabled
|
|
||||||
{
|
|
||||||
get => Timer.Enabled;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (!ValueIsEnabled(Global.Settings.DetectionTick))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Timer.Enabled = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int Interval => (int)(Timer.Interval / 1000);
|
|
||||||
|
|
||||||
private static bool ValueIsEnabled(int value)
|
|
||||||
{
|
|
||||||
return value != 0 && Range.InRange(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task TestAllDelayAsync()
|
|
||||||
{
|
|
||||||
if (!Monitor.TryEnter(TestAllLock))
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var tasks = Global.Settings.Server.Select(async s =>
|
|
||||||
{
|
|
||||||
await SemaphoreSlim.WaitAsync();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await s.PingAsync();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
SemaphoreSlim.Release();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Monitor.Exit(TestAllLock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void UpdateInterval()
|
|
||||||
{
|
|
||||||
Timer.Stop();
|
|
||||||
|
|
||||||
if (!ValueIsEnabled(Global.Settings.DetectionTick))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Timer.Interval = Global.Settings.DetectionTick * 1000;
|
|
||||||
Timer.Start();
|
|
||||||
|
|
||||||
TestAllDelayAsync().Forget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Handler
|
|
||||||
|
|
||||||
public static Dictionary<string, IServerUtil> ServerUtilDictionary { get; set; }
|
|
||||||
|
|
||||||
public static IServerUtil GetUtilByTypeName(string typeName)
|
public static IServerUtil GetUtilByTypeName(string typeName)
|
||||||
{
|
{
|
||||||
return ServerUtilDictionary[typeName];
|
return ServerUtilDictionary.GetValueOrDefault(typeName) ?? throw new NotSupportedException("Specified server type is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServerUtil? GetUtilByUriScheme(string scheme)
|
public static IServerUtil? GetUtilByUriScheme(string scheme)
|
||||||
@@ -130,7 +33,5 @@ namespace Netch.Utils
|
|||||||
{
|
{
|
||||||
return GetUtilByTypeName(typeName).ServerType;
|
return GetUtilByTypeName(typeName).ServerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user