v2rayN/v2rayN/ServiceLib/Services/SpeedtestService.cs

408 lines
14 KiB
C#
Raw Normal View History

using ReactiveUI;
using System.Diagnostics;
2019-10-11 06:15:20 +00:00
using System.Net;
using System.Net.Sockets;
2024-11-03 08:45:48 +00:00
2024-10-07 02:39:43 +00:00
namespace ServiceLib.Services
2019-10-11 06:15:20 +00:00
{
2024-10-07 02:39:43 +00:00
public class SpeedtestService
2019-10-11 06:15:20 +00:00
{
2024-02-02 02:39:07 +00:00
private Config? _config;
2024-10-08 05:47:13 +00:00
private Action<SpeedTestResult>? _updateFunc;
2024-08-19 10:15:54 +00:00
private bool _exitLoop = false;
2025-01-03 07:02:31 +00:00
private static readonly string _tag = "SpeedtestService";
2019-10-11 06:15:20 +00:00
2024-10-08 05:47:13 +00:00
public SpeedtestService(Config config, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<SpeedTestResult> updateFunc)
2019-10-11 06:15:20 +00:00
{
_config = config;
2024-10-08 05:47:13 +00:00
_updateFunc = updateFunc;
2019-10-11 06:15:20 +00:00
var lstSelected = new List<ServerTestItem>();
2022-01-28 13:05:41 +00:00
foreach (var it in selecteds)
{
if (it.ConfigType == EConfigType.Custom)
2023-01-31 06:05:31 +00:00
{
continue;
}
if (it.Port <= 0)
2023-02-09 07:17:52 +00:00
{
continue;
}
lstSelected.Add(new ServerTestItem()
2022-01-28 13:05:41 +00:00
{
IndexId = it.IndexId,
Address = it.Address,
Port = it.Port,
ConfigType = it.ConfigType
2022-01-28 13:05:41 +00:00
});
}
2023-01-31 06:05:31 +00:00
//clear test result
foreach (var it in lstSelected)
2023-01-31 06:05:31 +00:00
{
switch (actionType)
{
case ESpeedActionType.Tcping:
case ESpeedActionType.Realping:
2024-10-08 01:50:03 +00:00
UpdateFunc(it.IndexId, ResUI.Speedtesting, "");
ProfileExHandler.Instance.SetTestDelay(it.IndexId, "0");
2023-01-31 06:05:31 +00:00
break;
2023-04-14 12:49:36 +00:00
2023-01-31 06:05:31 +00:00
case ESpeedActionType.Speedtest:
2024-10-08 01:50:03 +00:00
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingWait);
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "0");
2023-01-31 06:05:31 +00:00
break;
2023-04-14 12:49:36 +00:00
2023-01-31 06:05:31 +00:00
case ESpeedActionType.Mixedtest:
2024-10-08 01:50:03 +00:00
UpdateFunc(it.IndexId, ResUI.Speedtesting, ResUI.SpeedtestingWait);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, "0");
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "0");
2023-01-31 06:05:31 +00:00
break;
}
}
MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop);
Task.Run(async () => { await RunAsync(actionType, lstSelected); });
}
private async Task RunAsync(ESpeedActionType actionType, List<ServerTestItem> lstSelected)
{
if (actionType == ESpeedActionType.Tcping)
{
await RunTcpingAsync(lstSelected);
return;
}
var pageSize = _config.SpeedTestItem.SpeedTestPageSize;
if (pageSize is <= 0 or > 1000)
{
pageSize = 1000;
}
List<List<ServerTestItem>> lstTest = new();
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard)).ToList();
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard).ToList();
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst1.Skip(num * pageSize).Take(pageSize).ToList());
}
for (var num = 0; num < (int)Math.Ceiling(lst2.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst2.Skip(num * pageSize).Take(pageSize).ToList());
}
foreach (var lst in lstTest)
2019-10-11 06:15:20 +00:00
{
switch (actionType)
{
case ESpeedActionType.Realping:
await RunRealPingAsync(lst);
break;
2023-04-14 12:49:36 +00:00
case ESpeedActionType.Speedtest:
await RunSpeedTestAsync(lst);
break;
2023-04-14 12:49:36 +00:00
case ESpeedActionType.Mixedtest:
await RunMixedTestAsync(lst);
break;
}
2023-04-14 12:49:36 +00:00
await Task.Delay(100);
2019-10-11 06:15:20 +00:00
}
UpdateFunc("", ResUI.SpeedtestingCompleted);
2019-10-11 06:15:20 +00:00
}
private void ExitLoop(string x)
2024-08-19 10:15:54 +00:00
{
2024-11-03 08:45:48 +00:00
if (_exitLoop) return;
2024-08-19 10:15:54 +00:00
_exitLoop = true;
UpdateFunc("", ResUI.SpeedtestingStop);
}
private async Task RunTcpingAsync(List<ServerTestItem> selecteds)
2019-10-11 06:15:20 +00:00
{
try
{
2024-02-02 02:39:07 +00:00
List<Task> tasks = [];
foreach (var it in selecteds)
2019-10-11 06:15:20 +00:00
{
2024-10-08 01:50:03 +00:00
if (it.ConfigType == EConfigType.Custom)
2019-10-11 06:15:20 +00:00
{
2024-02-02 02:39:07 +00:00
continue;
2019-10-11 06:15:20 +00:00
}
2024-10-22 09:50:42 +00:00
tasks.Add(Task.Run(async () =>
2019-10-11 06:15:20 +00:00
{
2024-02-02 02:39:07 +00:00
try
{
2024-12-05 06:05:12 +00:00
var time = await GetTcpingTime(it.Address, it.Port);
2024-02-02 02:39:07 +00:00
var output = FormatOut(time, Global.DelayUnit);
2019-10-11 06:15:20 +00:00
2024-10-08 01:50:03 +00:00
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
UpdateFunc(it.IndexId, output);
2024-02-02 02:39:07 +00:00
}
catch (Exception ex)
{
2025-01-03 07:02:31 +00:00
Logging.SaveLog(_tag, ex);
2024-02-02 02:39:07 +00:00
}
}));
}
Task.WaitAll([.. tasks]);
2019-10-11 06:15:20 +00:00
}
catch (Exception ex)
{
2025-01-03 07:02:31 +00:00
Logging.SaveLog(_tag, ex);
2019-10-11 06:15:20 +00:00
}
2024-02-02 02:39:07 +00:00
finally
2019-10-11 06:15:20 +00:00
{
2024-10-20 03:51:05 +00:00
await ProfileExHandler.Instance.SaveTo();
2024-02-02 02:39:07 +00:00
}
2019-10-11 06:15:20 +00:00
}
private async Task RunRealPingAsync(List<ServerTestItem> selecteds)
2019-10-11 06:15:20 +00:00
{
2024-12-05 06:05:12 +00:00
var pid = -1;
2019-10-11 06:15:20 +00:00
try
{
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
2021-05-11 12:11:59 +00:00
if (pid < 0)
{
2023-01-01 11:42:01 +00:00
UpdateFunc("", ResUI.FailedToRunCore);
2024-10-20 03:51:05 +00:00
return;
2021-05-11 12:11:59 +00:00
}
2019-10-21 02:35:54 +00:00
2024-12-05 06:05:12 +00:00
var downloadHandle = new DownloadService();
2023-05-21 08:29:57 +00:00
2023-02-19 05:34:22 +00:00
List<Task> tasks = new();
foreach (var it in selecteds)
2019-10-11 06:15:20 +00:00
{
2024-10-08 01:50:03 +00:00
if (!it.AllowTest)
2022-01-28 13:05:41 +00:00
{
continue;
}
2024-10-08 01:50:03 +00:00
if (it.ConfigType == EConfigType.Custom)
2019-10-11 06:15:20 +00:00
{
continue;
}
2023-04-28 13:10:13 +00:00
tasks.Add(Task.Run(async () =>
{
2020-03-11 05:22:36 +00:00
try
2019-10-11 06:15:20 +00:00
{
2024-12-06 06:01:11 +00:00
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
2024-12-05 06:05:12 +00:00
var output = await GetRealPingTime(downloadHandle, webProxy);
2022-03-13 10:47:17 +00:00
2024-10-08 01:50:03 +00:00
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
UpdateFunc(it.IndexId, output);
2024-12-05 06:05:12 +00:00
int.TryParse(output, out var delay);
2024-10-08 01:50:03 +00:00
it.Delay = delay;
2019-10-11 06:15:20 +00:00
}
2020-03-11 05:22:36 +00:00
catch (Exception ex)
2019-10-11 06:15:20 +00:00
{
2025-01-03 07:02:31 +00:00
Logging.SaveLog(_tag, ex);
2019-10-11 06:15:20 +00:00
}
2020-03-20 03:30:21 +00:00
}));
2019-10-11 06:15:20 +00:00
}
2020-03-20 03:30:21 +00:00
Task.WaitAll(tasks.ToArray());
2019-10-11 06:15:20 +00:00
}
catch (Exception ex)
{
2025-01-03 07:02:31 +00:00
Logging.SaveLog(_tag, ex);
2019-10-11 06:15:20 +00:00
}
finally
{
if (pid > 0)
{
2024-11-29 11:21:09 +00:00
await CoreHandler.Instance.CoreStopPid(pid);
}
2024-10-20 03:51:05 +00:00
await ProfileExHandler.Instance.SaveTo();
}
2019-10-11 06:15:20 +00:00
}
2023-01-06 11:23:36 +00:00
private async Task RunSpeedTestAsync(List<ServerTestItem> selecteds)
2019-10-11 06:15:20 +00:00
{
2024-12-05 06:05:12 +00:00
var pid = -1;
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
2021-05-11 12:11:59 +00:00
if (pid < 0)
{
2023-01-01 11:42:01 +00:00
UpdateFunc("", ResUI.FailedToRunCore);
2021-05-11 12:11:59 +00:00
return;
}
2019-11-02 03:07:31 +00:00
2024-12-05 06:05:12 +00:00
var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
2023-02-08 11:20:44 +00:00
2024-10-07 02:39:43 +00:00
DownloadService downloadHandle = new();
2019-10-11 06:15:20 +00:00
foreach (var it in selecteds)
2019-10-11 06:15:20 +00:00
{
2024-08-19 10:15:54 +00:00
if (_exitLoop)
2024-02-25 10:28:59 +00:00
{
2024-10-08 01:50:03 +00:00
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
2024-02-25 10:28:59 +00:00
continue;
}
2024-10-08 01:50:03 +00:00
if (!it.AllowTest)
{
2022-01-28 13:05:41 +00:00
continue;
}
2024-10-08 01:50:03 +00:00
if (it.ConfigType == EConfigType.Custom)
2020-04-23 08:20:30 +00:00
{
continue;
}
2023-02-08 11:20:44 +00:00
//if (it.delay < 0)
//{
// UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip);
// continue;
//}
2024-10-08 01:50:03 +00:00
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "-1");
UpdateFunc(it.IndexId, "", ResUI.Speedtesting);
2023-02-08 11:20:44 +00:00
2024-10-21 01:45:33 +00:00
var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
2023-02-08 11:20:44 +00:00
if (item is null) continue;
2024-12-06 06:01:11 +00:00
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
2023-02-08 11:20:44 +00:00
2024-10-07 02:39:43 +00:00
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
2023-02-08 11:20:44 +00:00
{
2024-12-05 06:05:12 +00:00
decimal.TryParse(msg, out var dec);
2023-02-08 11:20:44 +00:00
if (dec > 0)
{
2024-10-08 01:50:03 +00:00
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
2023-02-08 11:20:44 +00:00
}
2024-10-08 01:50:03 +00:00
UpdateFunc(it.IndexId, "", msg);
2023-02-08 11:20:44 +00:00
});
}
if (pid > 0)
{
2024-11-29 11:21:09 +00:00
await CoreHandler.Instance.CoreStopPid(pid);
2023-02-08 11:20:44 +00:00
}
2024-10-21 01:45:33 +00:00
await ProfileExHandler.Instance.SaveTo();
2023-02-08 11:20:44 +00:00
}
private async Task RunSpeedTestMulti(List<ServerTestItem> selecteds)
2023-02-08 11:20:44 +00:00
{
2024-12-05 06:05:12 +00:00
var pid = -1;
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
2023-02-08 11:20:44 +00:00
if (pid < 0)
{
UpdateFunc("", ResUI.FailedToRunCore);
return;
}
2024-12-05 06:05:12 +00:00
var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
2023-02-08 11:20:44 +00:00
2024-10-07 02:39:43 +00:00
DownloadService downloadHandle = new();
2023-02-08 11:20:44 +00:00
foreach (var it in selecteds)
2023-02-08 11:20:44 +00:00
{
2024-08-19 10:15:54 +00:00
if (_exitLoop)
2024-02-25 10:28:59 +00:00
{
2024-10-08 01:50:03 +00:00
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
2024-02-25 10:28:59 +00:00
continue;
}
2024-10-08 01:50:03 +00:00
if (!it.AllowTest)
2023-02-08 11:20:44 +00:00
{
continue;
}
2024-10-08 01:50:03 +00:00
if (it.ConfigType == EConfigType.Custom)
2023-01-31 06:13:25 +00:00
{
2023-01-06 11:23:36 +00:00
continue;
}
2024-10-08 01:50:03 +00:00
if (it.Delay < 0)
{
2024-10-08 01:50:03 +00:00
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
continue;
}
2024-10-08 01:50:03 +00:00
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "-1");
UpdateFunc(it.IndexId, "", ResUI.Speedtesting);
2023-01-01 11:42:01 +00:00
2024-10-21 01:45:33 +00:00
var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
2023-01-01 11:42:01 +00:00
if (item is null) continue;
2022-03-13 02:41:04 +00:00
2024-12-06 06:01:11 +00:00
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
2024-10-07 02:39:43 +00:00
_ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
2023-01-01 11:42:01 +00:00
{
2024-12-05 06:05:12 +00:00
decimal.TryParse(msg, out var dec);
2023-01-01 11:42:01 +00:00
if (dec > 0)
{
2024-10-08 01:50:03 +00:00
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
2023-01-01 11:42:01 +00:00
}
2024-10-08 01:50:03 +00:00
UpdateFunc(it.IndexId, "", msg);
2023-01-01 11:42:01 +00:00
});
2023-05-21 08:29:57 +00:00
await Task.Delay(2000);
}
2022-04-11 07:26:32 +00:00
2023-05-21 08:29:57 +00:00
await Task.Delay((timeout + 2) * 1000);
2023-02-08 11:20:44 +00:00
2023-01-01 11:42:01 +00:00
if (pid > 0)
{
2024-11-29 11:21:09 +00:00
await CoreHandler.Instance.CoreStopPid(pid);
2023-01-01 11:42:01 +00:00
}
2024-10-20 03:51:05 +00:00
await ProfileExHandler.Instance.SaveTo();
2019-10-11 06:15:20 +00:00
}
2023-02-08 11:20:44 +00:00
private async Task RunMixedTestAsync(List<ServerTestItem> selecteds)
2023-01-01 11:42:01 +00:00
{
await RunRealPingAsync(selecteds);
2019-10-11 06:15:20 +00:00
2023-05-21 08:29:57 +00:00
await Task.Delay(1000);
2023-01-06 11:23:36 +00:00
await RunSpeedTestMulti(selecteds);
2023-01-01 11:42:01 +00:00
}
2024-10-07 02:39:43 +00:00
private async Task<string> GetRealPingTime(DownloadService downloadHandle, IWebProxy webProxy)
2023-01-01 11:42:01 +00:00
{
2024-12-05 06:05:12 +00:00
var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
2024-02-19 09:43:36 +00:00
//string output = Utile.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status;
2023-04-28 13:10:13 +00:00
return FormatOut(responseTime, Global.DelayUnit);
2023-01-01 11:42:01 +00:00
}
2024-10-22 09:50:42 +00:00
private async Task<int> GetTcpingTime(string url, int port)
2019-10-11 06:15:20 +00:00
{
2024-12-05 06:05:12 +00:00
var responseTime = -1;
2019-10-11 06:15:20 +00:00
try
{
2024-12-05 06:05:12 +00:00
if (!IPAddress.TryParse(url, out var ipAddress))
{
2024-12-05 06:05:12 +00:00
var ipHostInfo = await Dns.GetHostEntryAsync(url);
2024-12-01 03:17:36 +00:00
ipAddress = ipHostInfo.AddressList.First();
}
2019-10-11 06:15:20 +00:00
2024-07-12 06:23:29 +00:00
var timer = Stopwatch.StartNew();
2019-10-11 06:15:20 +00:00
2023-02-19 05:34:22 +00:00
IPEndPoint endPoint = new(ipAddress, port);
using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
2024-07-12 06:23:29 +00:00
var result = clientSocket.BeginConnect(endPoint, null, null);
if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
throw new TimeoutException("connect timeout (5s): " + url);
clientSocket.EndConnect(result);
2019-10-11 06:15:20 +00:00
timer.Stop();
2024-07-05 07:28:28 +00:00
responseTime = (int)timer.Elapsed.TotalMilliseconds;
2019-10-11 06:15:20 +00:00
}
catch (Exception ex)
{
2025-01-03 07:02:31 +00:00
Logging.SaveLog(_tag, ex);
2019-10-11 06:15:20 +00:00
}
return responseTime;
}
2023-01-01 11:42:01 +00:00
private string FormatOut(object time, string unit)
{
return $"{time}";
}
private void UpdateFunc(string indexId, string delay, string speed = "")
{
2024-10-08 05:47:13 +00:00
_updateFunc?.Invoke(new() { IndexId = indexId, Delay = delay, Speed = speed });
2020-04-16 07:37:17 +00:00
}
2019-10-11 06:15:20 +00:00
}
2023-04-14 12:49:36 +00:00
}