diff --git a/v2rayN/ServiceLib/Models/Configs/ConfigItems.cs b/v2rayN/ServiceLib/Models/Configs/ConfigItems.cs
index e9e7f5f8..64902914 100644
--- a/v2rayN/ServiceLib/Models/Configs/ConfigItems.cs
+++ b/v2rayN/ServiceLib/Models/Configs/ConfigItems.cs
@@ -159,6 +159,8 @@ public class SpeedTestItem
public int MixedConcurrencyCount { get; set; }
public string IPAPIUrl { get; set; }
public string UdpTestTarget { get; set; }
+ public int BatchTesting { get; set; }
+ public int DelayInterval { get; set; }
}
[Serializable]
diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
index c3a00a51..e8a37ce5 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
+++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
@@ -4472,7 +4472,47 @@ namespace ServiceLib.Resx {
return ResourceManager.GetString("TbSettingsUdpTestUrl", resourceCulture);
}
}
+
+ ///
+ /// 查找类似 Batch Testing 的本地化字符串。
+ ///
+
+ public static string TbSettingsBatchTesting {
+ get {
+ return ResourceManager.GetString("TbSettingsBatchTesting", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Batch Testing Tip 的本地化字符串。
+ ///
+ public static string TbSettingsBatchTestingTip {
+ get {
+ return ResourceManager.GetString("TbSettingsBatchTestingTip", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Delay Interval 的本地化字符串。
+ ///
+
+ public static string TbSettingsDelayInterval {
+ get {
+ return ResourceManager.GetString("TbSettingsDelayInterval", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Delay Interval Tip 的本地化字符串。
+ ///
+
+ public static string TbSettingsDelayIntervalTip {
+ get {
+ return ResourceManager.GetString("TbSettingsDelayIntervalTip", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Auth user 的本地化字符串。
///
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
index 246417a6..a517e4c2 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
@@ -949,10 +949,10 @@
اندازه فونت
- یمقدار تاخیر تست سرعت منفرد
+ مقدار تاخیر تست سرعت منفرد
- /آدرس اینترنتی SpeedTest
+ آدرس اینترنتی SpeedTest
بالا و پایین حرکت کنید
@@ -1261,7 +1261,7 @@
فیلتر هسته
- فعال سازی
+ فعال
منبع فایل های جغرافیایی (اختیاری)
@@ -1705,10 +1705,22 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
Only for fetching self-signed certificates. This may expose you to MITM risks.
- Test Configurations UDP Delay
+ تست تاخیر کانفیگ ها از طریق UDP
- UDP Test Url
+ آدرس تست تاخیر UDP
+
+
+ تعداد تست همزمان در هر دسته
+
+
+ تعداد کانفیگ هایی که به صورت همزمان در هر دسته تست می شوند. مقدار 0 یعنی بدون محدودیت
+
+
+ تأخیر بین دسته ها بر حسب ثانیه
+
+
+ مدت زمان انتظار قبل از شروع دسته بعدی. مقدار 0 یعنی بدون تأخیر
Local outbound address (SendThrough)
@@ -1746,4 +1758,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
New Update
-
\ No newline at end of file
+
diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx
index 6cf545db..8de59863 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.resx
@@ -1710,6 +1710,18 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
UDP Test Url
+
+ Concurrent Tests Per Batch
+
+
+ Number of configurations tested simultaneously in each batch. Set 0 for unlimited.
+
+
+ Delay Between Batches (seconds)
+
+
+ Wait time before starting the next batch. Set 0 to disable delay.
+
Local outbound address (SendThrough)
@@ -1746,4 +1758,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
New Update
-
\ No newline at end of file
+
diff --git a/v2rayN/ServiceLib/Services/SpeedtestService.cs b/v2rayN/ServiceLib/Services/SpeedtestService.cs
index 08258160..4f92bb28 100644
--- a/v2rayN/ServiceLib/Services/SpeedtestService.cs
+++ b/v2rayN/ServiceLib/Services/SpeedtestService.cs
@@ -8,7 +8,6 @@ public class SpeedtestService(Config config, Func updateF
private readonly Config? _config = config;
private readonly Func? _updateFunc = updateFunc;
private static readonly ConcurrentBag _lstExitLoop = new();
-
public void RunLoop(ESpeedActionType actionType, List selecteds)
{
Task.Run(async () =>
@@ -44,7 +43,7 @@ public class SpeedtestService(Config config, Func updateF
switch (actionType)
{
case ESpeedActionType.Tcping:
- await RunTcpingAsync(lstSelected);
+ await RunTcpingAsync(lstSelected, exitLoopKey);
break;
case ESpeedActionType.Realping:
@@ -133,27 +132,67 @@ public class SpeedtestService(Config config, Func updateF
return lstSelected;
}
- private async Task RunTcpingAsync(List selecteds)
+ private async Task RunTcpingAsync(List selecteds, string exitLoopKey)
{
- List tasks = [];
- foreach (var it in selecteds)
+ int batchSize = _config.SpeedTestItem.BatchTesting;
+ int delay = _config.SpeedTestItem.DelayInterval;
+
+ if (batchSize <= 0)
{
- tasks.Add(Task.Run(async () =>
- {
- try
- {
- var responseTime = await GetTcpingTime(it.Address, it.Port);
-
- ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
- await UpdateFunc(it.IndexId, responseTime.ToString());
- }
- catch (Exception ex)
- {
- Logging.SaveLog(_tag, ex);
- }
- }));
+ batchSize = selecteds.Count;
+ }
+
+ for (int i = 0; i < selecteds.Count; i += batchSize)
+ {
+ if (ShouldStopTest(exitLoopKey))
+ {
+ return;
+ }
+
+ var batch = selecteds.Skip(i).Take(batchSize).ToList();
+
+ List tasks = [];
+
+ foreach (var it in batch)
+ {
+ if (ShouldStopTest(exitLoopKey))
+ {
+ return;
+ }
+
+ tasks.Add(Task.Run(async () =>
+ {
+ try
+ {
+ if (ShouldStopTest(exitLoopKey))
+ {
+ return;
+ }
+
+ var responseTime = await GetTcpingTime(it.Address, it.Port);
+
+ if (ShouldStopTest(exitLoopKey))
+ {
+ return;
+ }
+
+ ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
+ await UpdateFunc(it.IndexId, responseTime.ToString());
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ }
+ }));
+ }
+
+ await Task.WhenAll(tasks);
+
+ if (delay > 0 && i + batchSize < selecteds.Count)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(delay));
+ }
}
- await Task.WhenAll(tasks);
}
private async Task RunRealPingBatchAsync(List lstSelected, string exitLoopKey, int pageSize = 0)
@@ -210,26 +249,46 @@ public class SpeedtestService(Config config, Func updateF
}
await Task.Delay(1000);
- List tasks = new();
- foreach (var it in selecteds)
+ int batchSize = _config.SpeedTestItem.BatchTesting;
+ int delay = _config.SpeedTestItem.DelayInterval;
+
+ if (batchSize <= 0)
{
- if (!it.AllowTest)
- {
- await UpdateFunc(it.IndexId, ResUI.SpeedtestingSkip);
- continue;
- }
-
- if (ShouldStopTest(exitLoopKey))
- {
- return false;
- }
-
- tasks.Add(Task.Run(async () =>
- {
- await DoRealPing(it);
- }));
+ batchSize = selecteds.Count;
+ }
+
+ for (int i = 0; i < selecteds.Count; i += batchSize)
+ {
+ var batch = selecteds.Skip(i).Take(batchSize).ToList();
+
+ List tasks = new();
+
+ foreach (var it in batch)
+ {
+ if (!it.AllowTest)
+ {
+ await UpdateFunc(it.IndexId, ResUI.SpeedtestingSkip);
+ continue;
+ }
+
+ if (ShouldStopTest(exitLoopKey))
+ {
+ return false;
+ }
+
+ tasks.Add(Task.Run(async () =>
+ {
+ await DoRealPing(it);
+ }));
+ }
+
+ await Task.WhenAll(tasks);
+
+ if (delay > 0 && i + batchSize < selecteds.Count)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(delay));
+ }
}
- await Task.WhenAll(tasks);
}
catch (Exception ex)
{
@@ -291,25 +350,45 @@ public class SpeedtestService(Config config, Func updateF
}
await Task.Delay(1000);
- List tasks = new();
- foreach (var it in selecteds)
+ int batchSize = _config.SpeedTestItem.BatchTesting;
+ int delay = _config.SpeedTestItem.DelayInterval;
+
+ if (batchSize <= 0)
{
- if (!it.AllowTest)
- {
- continue;
- }
-
- if (ShouldStopTest(exitLoopKey))
- {
- return false;
- }
-
- tasks.Add(Task.Run(async () =>
- {
- await DoUdpTest(it);
- }));
+ batchSize = selecteds.Count;
+ }
+
+ for (int i = 0; i < selecteds.Count; i += batchSize)
+ {
+ var batch = selecteds.Skip(i).Take(batchSize).ToList();
+
+ List tasks = new();
+
+ foreach (var it in batch)
+ {
+ if (!it.AllowTest)
+ {
+ continue;
+ }
+
+ if (ShouldStopTest(exitLoopKey))
+ {
+ return false;
+ }
+
+ tasks.Add(Task.Run(async () =>
+ {
+ await DoUdpTest(it);
+ }));
+ }
+
+ await Task.WhenAll(tasks);
+
+ if (delay > 0 && i + batchSize < selecteds.Count)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(delay));
+ }
}
- await Task.WhenAll(tasks);
}
catch (Exception ex)
{
diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
index 122772c4..8a4802bf 100644
--- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
@@ -62,6 +62,8 @@ public class OptionSettingViewModel : MyReactiveObject
[Reactive] public string SpeedPingTestUrl { get; set; }
[Reactive] public string UdpTestTarget { get; set; }
[Reactive] public int MixedConcurrencyCount { get; set; }
+ [Reactive] public string BatchTesting { get; set; }
+ [Reactive] public string DelayInterval { get; set; }
[Reactive] public bool EnableHWA { get; set; }
[Reactive] public string SubConvertUrl { get; set; }
[Reactive] public int MainGirdOrientation { get; set; }
@@ -197,6 +199,8 @@ public class OptionSettingViewModel : MyReactiveObject
SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout;
SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl;
MixedConcurrencyCount = _config.SpeedTestItem.MixedConcurrencyCount;
+ BatchTesting = _config.SpeedTestItem.BatchTesting.ToString();
+ DelayInterval = _config.SpeedTestItem.DelayInterval.ToString();
SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl;
UdpTestTarget = _config.SpeedTestItem.UdpTestTarget;
EnableHWA = _config.GuiItem.EnableHWA;
@@ -371,6 +375,10 @@ public class OptionSettingViewModel : MyReactiveObject
_config.UiItem.CurrentFontFamily = CurrentFontFamily;
_config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout;
_config.SpeedTestItem.MixedConcurrencyCount = MixedConcurrencyCount;
+ int.TryParse(BatchTesting, out var batchTesting);
+ int.TryParse(DelayInterval, out var delayInterval);
+ _config.SpeedTestItem.BatchTesting = batchTesting;
+ _config.SpeedTestItem.DelayInterval = delayInterval;
_config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl;
_config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl;
_config.SpeedTestItem.UdpTestTarget = UdpTestTarget;
diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
index 2baea96c..fee63a8f 100644
--- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
@@ -375,7 +375,7 @@
+ RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
+
+
+
+
+
+
+
+
+
+
+
this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.UdpTestTarget, v => v.cmbUdpTestTarget.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.MixedConcurrencyCount, v => v.cmbMixedConcurrencyCount.SelectedValue).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.BatchTesting, v => v.txtBatchTesting.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.DelayInterval, v => v.txtDelayInterval.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel,
vm => vm.MainGirdOrientation, view => view.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);
diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml
index df46d963..b7f34f15 100644
--- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml
+++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml
@@ -66,6 +66,8 @@
+
+
@@ -595,6 +597,8 @@
+
+
@@ -869,17 +873,65 @@
Margin="{StaticResource Margin8}"
IsEditable="True"
Style="{StaticResource DefComboBox}" />
-
+
+
+
+
+
+
+
vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.UdpTestTarget, v => v.cmbUdpTestTarget.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.MixedConcurrencyCount, v => v.cmbMixedConcurrencyCount.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.BatchTesting, v => v.txtBatchTesting.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.DelayInterval, v => v.txtDelayInterval.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableHWA, v => v.togEnableHWA.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);