mirror of
https://github.com/2dust/v2rayN.git
synced 2026-09-26 08:32:08 +03:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d433aa3b04 | |||
| 91a7ed65d5 | |||
| 984e685ab3 | |||
| 29f99ceacc | |||
| 93d8174dbe | |||
| fa1e201c76 | |||
| e1cb99cd6e |
@@ -7,17 +7,17 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="12.0.0" />
|
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="12.0.0" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="12.1.2" />
|
<PackageVersion Include="Avalonia.Desktop" Version="12.1.3" />
|
||||||
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
|
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
|
||||||
<PackageVersion Include="DialogHost.Avalonia" Version="0.12.3" />
|
<PackageVersion Include="DialogHost.Avalonia" Version="0.12.3" />
|
||||||
<PackageVersion Include="IPNetwork2" Version="4.3.0" />
|
<PackageVersion Include="IPNetwork2" Version="4.3.0" />
|
||||||
<PackageVersion Include="ReactiveUI.Avalonia" Version="12.1.2" />
|
<PackageVersion Include="ReactiveUI.Avalonia" Version="12.1.2" />
|
||||||
<PackageVersion Include="CliWrap" Version="3.10.5" />
|
<PackageVersion Include="CliWrap" Version="3.10.5" />
|
||||||
<PackageVersion Include="Downloader" Version="5.9.6" />
|
<PackageVersion Include="Downloader" Version="5.9.8" />
|
||||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
|
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
|
||||||
<PackageVersion Include="MaterialDesignThemes" Version="5.3.2" />
|
<PackageVersion Include="MaterialDesignThemes" Version="5.3.2" />
|
||||||
<PackageVersion Include="QRCoder" Version="1.8.0" />
|
<PackageVersion Include="QRCoder" Version="1.8.0" />
|
||||||
<PackageVersion Include="ReactiveUI" Version="24.2.0" />
|
<PackageVersion Include="ReactiveUI" Version="24.3.0" />
|
||||||
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="3.2.0" />
|
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="3.2.0" />
|
||||||
<PackageVersion Include="ReactiveUI.WPF" Version="24.2.0" />
|
<PackageVersion Include="ReactiveUI.WPF" Version="24.2.0" />
|
||||||
<PackageVersion Include="Semi.Avalonia" Version="12.1.0.1" />
|
<PackageVersion Include="Semi.Avalonia" Version="12.1.0.1" />
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ namespace ServiceLib.Handler.SysProxy;
|
|||||||
public static class SysProxyHandler
|
public static class SysProxyHandler
|
||||||
{
|
{
|
||||||
private static readonly string _tag = "SysProxyHandler";
|
private static readonly string _tag = "SysProxyHandler";
|
||||||
|
private static readonly Lazy<PacManager> _pacManager = new(() => new PacManager());
|
||||||
|
|
||||||
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
|
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
|
||||||
{
|
{
|
||||||
@@ -56,7 +57,7 @@ public static class SysProxyHandler
|
|||||||
|
|
||||||
if (type != ESysProxyType.Pac && Utils.IsWindows())
|
if (type != ESysProxyType.Pac && Utils.IsWindows())
|
||||||
{
|
{
|
||||||
PacManager.Instance.Stop();
|
_pacManager.Value.Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -110,7 +111,7 @@ public static class SysProxyHandler
|
|||||||
private static async Task SetWindowsProxyPac(int port)
|
private static async Task SetWindowsProxyPac(int port)
|
||||||
{
|
{
|
||||||
var portPac = AppManager.Instance.GetLocalPort(EInboundProtocol.pac);
|
var portPac = AppManager.Instance.GetLocalPort(EInboundProtocol.pac);
|
||||||
await PacManager.Instance.StartAsync(port, portPac);
|
await _pacManager.Value.StartAsync(port, portPac);
|
||||||
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
|
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
|
||||||
ProxySettingWindows.SetProxy(strProxy, "", 4);
|
ProxySettingWindows.SetProxy(strProxy, "", 4);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class HttpClientHelper
|
|||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string?> TryGetAsync(string url)
|
public async Task<string?> TryGetAsync(string url, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (url.IsNullOrEmpty())
|
if (url.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
@@ -31,8 +31,12 @@ public class HttpClientHelper
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await httpClient.GetAsync(url);
|
var response = await httpClient.GetAsync(url, cancellationToken);
|
||||||
return await response.Content.ReadAsStringAsync();
|
return await response.Content.ReadAsStringAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -75,15 +75,28 @@ public sealed class SQLiteHelper
|
|||||||
|
|
||||||
public async Task DisposeDbConnectionAsync()
|
public async Task DisposeDbConnectionAsync()
|
||||||
{
|
{
|
||||||
await Task.Factory.StartNew(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
_db?.Close();
|
try
|
||||||
_db?.Dispose();
|
{
|
||||||
_db = null;
|
_db?.Close();
|
||||||
|
_db?.Dispose();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_db = null;
|
||||||
|
}
|
||||||
|
|
||||||
_dbAsync?.GetConnection()?.Close();
|
try
|
||||||
_dbAsync?.GetConnection()?.Dispose();
|
{
|
||||||
_dbAsync = null;
|
var conn = _dbAsync?.GetConnection();
|
||||||
|
conn?.Close();
|
||||||
|
conn?.Dispose();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_dbAsync = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,36 +2,37 @@ namespace ServiceLib.Manager;
|
|||||||
|
|
||||||
public class PacManager
|
public class PacManager
|
||||||
{
|
{
|
||||||
private static readonly Lazy<PacManager> _instance = new(() => new PacManager());
|
private const string Tag = "PacManager";
|
||||||
public static PacManager Instance => _instance.Value;
|
private CancellationTokenSource? _cts;
|
||||||
|
|
||||||
private int _httpPort;
|
|
||||||
private int _pacPort;
|
private int _pacPort;
|
||||||
private TcpListener? _tcpListener;
|
private TcpListener? _tcpListener;
|
||||||
private byte[] _writeContent;
|
private byte[] _writeContent = [];
|
||||||
private bool _isRunning;
|
|
||||||
private bool _needRestart = true;
|
|
||||||
|
|
||||||
public async Task StartAsync(int httpPort, int pacPort)
|
public async Task StartAsync(int httpPort, int pacPort)
|
||||||
{
|
{
|
||||||
_needRestart = httpPort != _httpPort || pacPort != _pacPort || !_isRunning;
|
var content = await InitText(httpPort);
|
||||||
|
_writeContent = content;
|
||||||
|
|
||||||
_httpPort = httpPort;
|
if (_tcpListener is not null && _pacPort == pacPort)
|
||||||
_pacPort = pacPort;
|
|
||||||
|
|
||||||
await InitText();
|
|
||||||
|
|
||||||
if (_needRestart)
|
|
||||||
{
|
{
|
||||||
Stop();
|
return;
|
||||||
RunListener();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stop();
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
var listener = TcpListener.Create(pacPort);
|
||||||
|
listener.Start();
|
||||||
|
|
||||||
|
_cts = cts;
|
||||||
|
_pacPort = pacPort;
|
||||||
|
_tcpListener = listener;
|
||||||
|
_ = ListenLoopAsync(listener, cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InitText()
|
private async Task<byte[]> InitText(int httpPort)
|
||||||
{
|
{
|
||||||
var customSystemProxyPacPath = AppManager.Instance.Config.SystemProxyItem?.CustomSystemProxyPacPath;
|
var customSystemProxyPacPath = AppManager.Instance.Config.SystemProxyItem.CustomSystemProxyPacPath;
|
||||||
var fileName = (customSystemProxyPacPath.IsNotEmpty() && File.Exists(customSystemProxyPacPath))
|
var fileName = customSystemProxyPacPath.IsNotEmpty() && File.Exists(customSystemProxyPacPath)
|
||||||
? customSystemProxyPacPath
|
? customSystemProxyPacPath
|
||||||
: Path.Combine(Utils.GetConfigPath(), "pac.txt");
|
: Path.Combine(Utils.GetConfigPath(), "pac.txt");
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ public class PacManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pacText = await File.ReadAllTextAsync(fileName);
|
var pacText = await File.ReadAllTextAsync(fileName);
|
||||||
pacText = pacText.Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
|
pacText = pacText.Replace("__PROXY__", $"PROXY 127.0.0.1:{httpPort};DIRECT;");
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.AppendLine("HTTP/1.0 200 OK");
|
sb.AppendLine("HTTP/1.0 200 OK");
|
||||||
@@ -54,59 +55,43 @@ public class PacManager
|
|||||||
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
|
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
sb.Append(pacText);
|
sb.Append(pacText);
|
||||||
_writeContent = Encoding.UTF8.GetBytes(sb.ToString());
|
return Encoding.UTF8.GetBytes(sb.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunListener()
|
private async Task ListenLoopAsync(TcpListener listener, CancellationToken token)
|
||||||
{
|
{
|
||||||
_tcpListener = TcpListener.Create(_pacPort);
|
var buffer = new byte[1024];
|
||||||
_isRunning = true;
|
try
|
||||||
_tcpListener.Start();
|
|
||||||
Task.Factory.StartNew(async () =>
|
|
||||||
{
|
{
|
||||||
while (_isRunning)
|
while (!token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
try
|
using var client = await listener.AcceptTcpClientAsync(token).ConfigureAwait(false);
|
||||||
{
|
await using var stream = client.GetStream();
|
||||||
if (!_tcpListener.Pending())
|
_ = await stream.ReadAsync(buffer, token).ConfigureAwait(false);
|
||||||
{
|
await stream.WriteAsync(_writeContent, token).ConfigureAwait(false);
|
||||||
await Task.Delay(10);
|
await stream.FlushAsync(token).ConfigureAwait(false);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = await _tcpListener.AcceptTcpClientAsync();
|
|
||||||
await Task.Run(() => WriteContent(client));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, TaskCreationOptions.LongRunning);
|
}
|
||||||
}
|
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
||||||
|
{
|
||||||
private void WriteContent(TcpClient client)
|
}
|
||||||
{
|
catch (Exception ex)
|
||||||
var stream = client.GetStream();
|
{
|
||||||
stream.Write(_writeContent, 0, _writeContent.Length);
|
Logging.SaveLog(Tag, ex);
|
||||||
stream.Flush();
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
listener.Stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
if (_tcpListener == null)
|
_cts?.Cancel();
|
||||||
{
|
_tcpListener?.Stop();
|
||||||
return;
|
_cts?.Dispose();
|
||||||
}
|
_cts = null;
|
||||||
try
|
_pacPort = 0;
|
||||||
{
|
_tcpListener = null;
|
||||||
_isRunning = false;
|
|
||||||
_tcpListener.Stop();
|
|
||||||
_tcpListener = null;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,23 +12,17 @@ public class TaskManager
|
|||||||
_config = config;
|
_config = config;
|
||||||
_updateFunc = updateFunc;
|
_updateFunc = updateFunc;
|
||||||
|
|
||||||
_ = Task.Factory.StartNew(
|
Task.Run(ScheduledTasks);
|
||||||
ScheduledTasks,
|
|
||||||
CancellationToken.None,
|
|
||||||
TaskCreationOptions.LongRunning,
|
|
||||||
TaskScheduler.Default);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ScheduledTasks()
|
private async Task ScheduledTasks()
|
||||||
{
|
{
|
||||||
Logging.SaveLog("Setup Scheduled Tasks");
|
Logging.SaveLog("Setup Scheduled Tasks");
|
||||||
|
|
||||||
|
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
|
||||||
var numOfExecuted = 1;
|
var numOfExecuted = 1;
|
||||||
while (true)
|
while (await timer.WaitForNextTickAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
//1 minute
|
|
||||||
await Task.Delay(1000 * 60);
|
|
||||||
|
|
||||||
//Execute once 1 minute
|
//Execute once 1 minute
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
Generated
+1
-1
@@ -2149,7 +2149,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Not Support 的本地化字符串。
|
/// 查找类似 Not Supported 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string MsgNotSupport {
|
public static string MsgNotSupport {
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -365,9 +365,16 @@ public partial class CoreConfigSingboxService
|
|||||||
rule4ExpectedIPs = JsonUtils.DeepCopy(rule);
|
rule4ExpectedIPs = JsonUtils.DeepCopy(rule);
|
||||||
rule4ExpectedIPs.geosite = regionGeosite;
|
rule4ExpectedIPs.geosite = regionGeosite;
|
||||||
}
|
}
|
||||||
|
if (rule.geosite?.Count > 0
|
||||||
|
|| rule.domain?.Count > 0
|
||||||
|
|| rule.domain_keyword?.Count > 0
|
||||||
|
|| rule.domain_regex?.Count > 0
|
||||||
|
|| rule.domain_suffix?.Count > 0)
|
||||||
|
{
|
||||||
|
AddRules(rule, item, directDnsList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (rule.geosite?.Count > 0 || rule.domain?.Count > 0)
|
|
||||||
{
|
{
|
||||||
AddRules(rule, item, directDnsList);
|
AddRules(rule, item, directDnsList);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||||||
private readonly Config? _config = config;
|
private readonly Config? _config = config;
|
||||||
private readonly Func<SpeedTestResult, Task>? _updateFunc = updateFunc;
|
private readonly Func<SpeedTestResult, Task>? _updateFunc = updateFunc;
|
||||||
private readonly Lock _runLock = new();
|
private readonly Lock _runLock = new();
|
||||||
private CancellationTokenSource? _runCts;
|
private readonly List<CancellationTokenSource> _runCtsList = [];
|
||||||
private readonly int _speedTestPageSize = config.SpeedTestItem.SpeedTestPageSize ?? Global.SpeedTestPageSize;
|
private readonly int _speedTestPageSize = config.SpeedTestItem.SpeedTestPageSize ?? Global.SpeedTestPageSize;
|
||||||
private readonly TimeSpan _delayInterval = TimeSpan.FromSeconds(config.SpeedTestItem.SpeedTestDelayInterval ?? 1);
|
private readonly TimeSpan _delayInterval = TimeSpan.FromSeconds(config.SpeedTestItem.SpeedTestDelayInterval ?? 1);
|
||||||
|
|
||||||
@@ -18,11 +18,9 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||||||
|
|
||||||
lock (_runLock)
|
lock (_runLock)
|
||||||
{
|
{
|
||||||
_runCts?.Cancel();
|
|
||||||
|
|
||||||
runCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
runCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||||
|
|
||||||
_runCts = runCts;
|
_runCtsList.Add(runCts);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RunLoopAsync(actionType, selecteds, runCts);
|
return RunLoopAsync(actionType, selecteds, runCts);
|
||||||
@@ -30,17 +28,30 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||||||
|
|
||||||
public void ExitLoop()
|
public void ExitLoop()
|
||||||
{
|
{
|
||||||
CancellationTokenSource? runCts;
|
var counter = 0;
|
||||||
|
List<CancellationTokenSource> listToCancel;
|
||||||
|
|
||||||
lock (_runLock)
|
lock (_runLock)
|
||||||
{
|
{
|
||||||
runCts = _runCts;
|
listToCancel = _runCtsList.ToList();
|
||||||
|
counter = listToCancel.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runCts is not null)
|
foreach (var cts in listToCancel)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cts.Cancel();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter > 0)
|
||||||
{
|
{
|
||||||
_ = UpdateFunc("", ResUI.SpeedtestingStop);
|
_ = UpdateFunc("", ResUI.SpeedtestingStop);
|
||||||
runCts.Cancel();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,10 +78,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||||||
|
|
||||||
lock (_runLock)
|
lock (_runLock)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(_runCts, runCts))
|
_runCtsList.Remove(runCts);
|
||||||
{
|
|
||||||
_runCts = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runCts.Dispose();
|
runCts.Dispose();
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ namespace ServiceLib.Services.Statistics;
|
|||||||
public class StatisticsSingboxService
|
public class StatisticsSingboxService
|
||||||
{
|
{
|
||||||
private readonly Config _config;
|
private readonly Config _config;
|
||||||
private bool _exitFlag;
|
private CancellationTokenSource? _cts;
|
||||||
private ClientWebSocket? webSocket;
|
|
||||||
private readonly Func<ServerSpeedItem, Task>? _updateFunc;
|
private readonly Func<ServerSpeedItem, Task>? _updateFunc;
|
||||||
private string Url => $"ws://{Global.Loopback}:{AppManager.Instance.StatePort2}/traffic";
|
private string Url => $"ws://{Global.Loopback}:{AppManager.Instance.StatePort2}/traffic";
|
||||||
private static readonly string _tag = "StatisticsSingboxService";
|
private static readonly string _tag = "StatisticsSingboxService";
|
||||||
@@ -15,40 +14,17 @@ public class StatisticsSingboxService
|
|||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_updateFunc = updateFunc;
|
_updateFunc = updateFunc;
|
||||||
_exitFlag = false;
|
|
||||||
|
|
||||||
_ = Task.Factory.StartNew(
|
Task.Run(Run);
|
||||||
Run,
|
|
||||||
CancellationToken.None,
|
|
||||||
TaskCreationOptions.LongRunning,
|
|
||||||
TaskScheduler.Default);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Init()
|
|
||||||
{
|
|
||||||
await Task.Delay(5000);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (webSocket == null)
|
|
||||||
{
|
|
||||||
webSocket = new ClientWebSocket();
|
|
||||||
await webSocket.ConnectAsync(new Uri(Url), CancellationToken.None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Close()
|
public void Close()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_exitFlag = true;
|
_cts?.Cancel();
|
||||||
if (webSocket != null)
|
_cts?.Dispose();
|
||||||
{
|
_cts = null;
|
||||||
webSocket.Abort();
|
|
||||||
webSocket = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -58,53 +34,63 @@ public class StatisticsSingboxService
|
|||||||
|
|
||||||
private async Task Run()
|
private async Task Run()
|
||||||
{
|
{
|
||||||
await Init();
|
Close();
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
var token = _cts.Token;
|
||||||
|
|
||||||
while (!_exitFlag)
|
while (!token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await Task.Delay(1000);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!AppManager.Instance.IsRunningCore(ECoreType.sing_box))
|
if (!AppManager.Instance.IsRunningCore(ECoreType.sing_box))
|
||||||
{
|
{
|
||||||
|
await Task.Delay(1000, token).ConfigureAwait(false);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (webSocket != null)
|
using var ws = new ClientWebSocket();
|
||||||
|
await ws.ConnectAsync(new Uri(Url), token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var buffer = new byte[1024];
|
||||||
|
while (ws.State == WebSocketState.Open
|
||||||
|
&& !token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
if (webSocket.State is WebSocketState.Aborted or WebSocketState.Closed)
|
var res = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), token);
|
||||||
|
if (res.MessageType == WebSocketMessageType.Close)
|
||||||
{
|
{
|
||||||
webSocket.Abort();
|
break;
|
||||||
webSocket = null;
|
}
|
||||||
await Init();
|
using var ms = new MemoryStream();
|
||||||
continue;
|
ms.Write(buffer, 0, res.Count);
|
||||||
|
while (!res.EndOfMessage)
|
||||||
|
{
|
||||||
|
res = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), token).ConfigureAwait(false);
|
||||||
|
ms.Write(buffer, 0, res.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (webSocket.State != WebSocketState.Open)
|
var result = Encoding.UTF8.GetString(ms.ToArray());
|
||||||
|
if (!result.IsNotEmpty())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
ParseOutput(result, out var up, out var down);
|
||||||
|
|
||||||
var buffer = new byte[1024];
|
if (_updateFunc != null)
|
||||||
var res = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
|
||||||
while (!res.CloseStatus.HasValue)
|
|
||||||
{
|
{
|
||||||
var result = Encoding.UTF8.GetString(buffer, 0, res.Count);
|
await _updateFunc.Invoke(new ServerSpeedItem
|
||||||
if (result.IsNotEmpty())
|
|
||||||
{
|
{
|
||||||
ParseOutput(result, out var up, out var down);
|
ProxyUp = (long)(up / 1000),
|
||||||
|
ProxyDown = (long)(down / 1000),
|
||||||
await _updateFunc?.Invoke(new ServerSpeedItem()
|
}).ConfigureAwait(false);
|
||||||
{
|
|
||||||
ProxyUp = (long)(up / 1000),
|
|
||||||
ProxyDown = (long)(down / 1000)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
res = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
await Task.Delay(3000, token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,33 +5,42 @@ public class StatisticsXrayService
|
|||||||
private const long linkBase = 1024;
|
private const long linkBase = 1024;
|
||||||
private ServerSpeedItem _serverSpeedItem = new();
|
private ServerSpeedItem _serverSpeedItem = new();
|
||||||
private readonly Config _config;
|
private readonly Config _config;
|
||||||
private bool _exitFlag;
|
private CancellationTokenSource? _cts;
|
||||||
private readonly Func<ServerSpeedItem, Task>? _updateFunc;
|
private readonly Func<ServerSpeedItem, Task>? _updateFunc;
|
||||||
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort}/debug/vars";
|
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort}/debug/vars";
|
||||||
|
private static readonly string _tag = "StatisticsXrayService";
|
||||||
|
|
||||||
public StatisticsXrayService(Config config, Func<ServerSpeedItem, Task> updateFunc)
|
public StatisticsXrayService(Config config, Func<ServerSpeedItem, Task> updateFunc)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_updateFunc = updateFunc;
|
_updateFunc = updateFunc;
|
||||||
_exitFlag = false;
|
|
||||||
|
|
||||||
_ = Task.Factory.StartNew(
|
Task.Run(Run);
|
||||||
Run,
|
|
||||||
CancellationToken.None,
|
|
||||||
TaskCreationOptions.LongRunning,
|
|
||||||
TaskScheduler.Default);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Close()
|
public void Close()
|
||||||
{
|
{
|
||||||
_exitFlag = true;
|
try
|
||||||
|
{
|
||||||
|
_cts?.Cancel();
|
||||||
|
_cts?.Dispose();
|
||||||
|
_cts = null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Run()
|
private async Task Run()
|
||||||
{
|
{
|
||||||
while (!_exitFlag)
|
Close();
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
var token = _cts.Token;
|
||||||
|
|
||||||
|
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
|
||||||
|
while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
await Task.Delay(1000);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (AppManager.Instance.RunningCoreType != ECoreType.Xray)
|
if (AppManager.Instance.RunningCoreType != ECoreType.Xray)
|
||||||
@@ -39,16 +48,20 @@ public class StatisticsXrayService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await HttpClientHelper.Instance.TryGetAsync(Url);
|
var result = await HttpClientHelper.Instance.TryGetAsync(Url, token);
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
var server = ParseOutput(result) ?? new ServerSpeedItem();
|
var server = ParseOutput(result) ?? new ServerSpeedItem();
|
||||||
await _updateFunc?.Invoke(server);
|
await _updateFunc!.Invoke(server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// ignored
|
await Task.Delay(3000, token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,11 +42,21 @@ public partial class CheckUpdateViewModel : MyReactiveObject
|
|||||||
this.WhenAnyValue(x => x.EnableUpdateViaProxy)
|
this.WhenAnyValue(x => x.EnableUpdateViaProxy)
|
||||||
.Subscribe(c => _ = OnUpdateViaProxyChanged());
|
.Subscribe(c => _ = OnUpdateViaProxyChanged());
|
||||||
|
|
||||||
RefreshCheckUpdateItems();
|
AppEvents.HasUpdateNotified
|
||||||
|
.AsObservable()
|
||||||
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
|
.Subscribe(bl => RefreshCheckUpdateItems(bl));
|
||||||
|
|
||||||
|
RefreshCheckUpdateItems(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshCheckUpdateItems()
|
private void RefreshCheckUpdateItems(bool hasUpdate)
|
||||||
{
|
{
|
||||||
|
if (!hasUpdate)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var models = CoreInfoManager.Instance.GetCheckUpdateCoreTypes()
|
var models = CoreInfoManager.Instance.GetCheckUpdateCoreTypes()
|
||||||
.Select(t => GetCheckUpdateModel(t))
|
.Select(t => GetCheckUpdateModel(t))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|||||||
@@ -30,12 +30,7 @@ public partial class ClashConnectionsViewModel : MyReactiveObject
|
|||||||
cancelDisposable.DisposeWith(disposables);
|
cancelDisposable.DisposeWith(disposables);
|
||||||
var token = cancelDisposable.Token;
|
var token = cancelDisposable.Token;
|
||||||
|
|
||||||
Task.Factory.StartNew(
|
Task.Run(() => GetClashConnectionsTask(token));
|
||||||
async () => await GetClashConnectionsTask(token),
|
|
||||||
token,
|
|
||||||
TaskCreationOptions.LongRunning,
|
|
||||||
TaskScheduler.Default
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,9 +124,9 @@ public partial class ClashConnectionsViewModel : MyReactiveObject
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var numOfExecuted = 1;
|
var numOfExecuted = 1;
|
||||||
while (!token.IsCancellationRequested)
|
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
|
||||||
|
while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
await Task.Delay(1000, token);
|
|
||||||
numOfExecuted++;
|
numOfExecuted++;
|
||||||
if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar &&
|
if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar &&
|
||||||
AppManager.Instance.IsRunningCore(ECoreType.sing_box)))
|
AppManager.Instance.IsRunningCore(ECoreType.sing_box)))
|
||||||
|
|||||||
@@ -61,12 +61,7 @@ public partial class ClashProxiesViewModel : MyReactiveObject
|
|||||||
cancelDisposable.DisposeWith(disposables);
|
cancelDisposable.DisposeWith(disposables);
|
||||||
var token = cancelDisposable.Token;
|
var token = cancelDisposable.Token;
|
||||||
|
|
||||||
Task.Factory.StartNew(
|
Task.Run(() => GetClashProxiesTask(token));
|
||||||
async () => await GetClashProxiesTask(token),
|
|
||||||
token,
|
|
||||||
TaskCreationOptions.LongRunning,
|
|
||||||
TaskScheduler.Default
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,9 +108,9 @@ public partial class ClashProxiesViewModel : MyReactiveObject
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var numOfExecuted = 1;
|
var numOfExecuted = 1;
|
||||||
while (!token.IsCancellationRequested)
|
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
|
||||||
|
while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
await Task.Delay(1000, token);
|
|
||||||
numOfExecuted++;
|
numOfExecuted++;
|
||||||
if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar &&
|
if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar &&
|
||||||
AppManager.Instance.IsRunningCore(ECoreType.sing_box)))
|
AppManager.Instance.IsRunningCore(ECoreType.sing_box)))
|
||||||
|
|||||||
Reference in New Issue
Block a user