diff --git a/v2rayN/v2rayN/Base/DownloaderHelper.cs b/v2rayN/v2rayN/Base/DownloaderHelper.cs new file mode 100644 index 00000000..15b27feb --- /dev/null +++ b/v2rayN/v2rayN/Base/DownloaderHelper.cs @@ -0,0 +1,89 @@ +using Downloader; +using System.Net; + +namespace v2rayN.Base +{ + internal class DownloaderHelper + { + private static readonly Lazy _instance = new Lazy(() => new()); + public static DownloaderHelper Instance => _instance.Value; + + public DownloaderHelper() + { + } + + public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress progress, int timeout) + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentNullException("url"); + } + + var cancellationToken = new CancellationTokenSource(); + cancellationToken.CancelAfter(timeout * 1000); + + var downloadOpt = new DownloadConfiguration() + { + Timeout = timeout * 1000, + MaxTryAgainOnFailover = 2, + RequestConfiguration = + { + Timeout= timeout * 1000, + Proxy = webProxy + } + }; + + DateTime totalDatetime = DateTime.Now; + int totalSecond = 0; + var hasValue = false; + double maxSpeed = 0; + var downloader = new DownloadService(downloadOpt); + //downloader.DownloadStarted += (sender, value) => + //{ + // if (progress != null) + // { + // progress.Report("Start download data..."); + // } + //}; + downloader.DownloadProgressChanged += (sender, value) => + { + TimeSpan ts = (DateTime.Now - totalDatetime); + if (progress != null && ts.Seconds > totalSecond) + { + hasValue = true; + totalSecond = ts.Seconds; + if (value.BytesPerSecondSpeed > maxSpeed) + { + maxSpeed = value.BytesPerSecondSpeed; + var speed = (maxSpeed / 1000 / 1000).ToString("#0.0"); + progress.Report(speed); + } + } + }; + downloader.DownloadFileCompleted += (sender, value) => + { + if (progress != null) + { + if (!hasValue && value.Error != null) + { + progress.Report(value.Error?.Message); + } + } + }; + progress.Report("......"); + + await downloader.DownloadFileTaskAsync(address: url, cancellationToken: cancellationToken.Token); + //var stream = await downloader.DownloadFileTaskAsync(url); + + //using (StreamReader reader = new StreamReader(stream)) + //{ + // string text = reader.ReadToEnd(); + // stream.Dispose(); + //} + + downloader.Dispose(); + downloader = null; + downloadOpt = null; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Base/SqliteHandler.cs b/v2rayN/v2rayN/Base/SqliteHelper.cs similarity index 100% rename from v2rayN/v2rayN/Base/SqliteHandler.cs rename to v2rayN/v2rayN/Base/SqliteHelper.cs diff --git a/v2rayN/v2rayN/Global.cs b/v2rayN/v2rayN/Global.cs index 0d92c1b3..83aaeed1 100644 --- a/v2rayN/v2rayN/Global.cs +++ b/v2rayN/v2rayN/Global.cs @@ -17,7 +17,6 @@ public const string tuicCoreUrl = "https://github.com/EAimTY/tuic/releases"; public const string singboxCoreUrl = "https://github.com/SagerNet/sing-box/releases"; public const string geoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat"; - public const string SpeedTestUrl = @"http://cachefly.cachefly.net/10mb.test"; public const string SpeedPingTestUrl = @"https://www.google.com/generate_204"; public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"; @@ -116,6 +115,7 @@ public static readonly List TunMtus = new List { "9000", "1500" }; public static readonly List TunStacks = new List { "gvisor", "system" }; public static readonly List PresetMsgFilters = new List { "^(?!.*proxy).*$", "^(?!.*direct).*$", "" }; + public static readonly List SpeedTestUrls = new List { @"http://cachefly.cachefly.net/100mb.test", @"http://cachefly.cachefly.net/10mb.test" }; #endregion diff --git a/v2rayN/v2rayN/Handler/ConfigHandler.cs b/v2rayN/v2rayN/Handler/ConfigHandler.cs index d5a3b9e9..23628aa3 100644 --- a/v2rayN/v2rayN/Handler/ConfigHandler.cs +++ b/v2rayN/v2rayN/Handler/ConfigHandler.cs @@ -150,14 +150,6 @@ namespace v2rayN.Handler { config.constItem = new ConstItem(); } - if (Utils.IsNullOrEmpty(config.constItem.speedTestUrl)) - { - config.constItem.speedTestUrl = Global.SpeedTestUrl; - } - if (Utils.IsNullOrEmpty(config.constItem.speedPingTestUrl)) - { - config.constItem.speedPingTestUrl = Global.SpeedPingTestUrl; - } if (Utils.IsNullOrEmpty(config.constItem.defIEProxyExceptions)) { config.constItem.defIEProxyExceptions = Global.IEProxyExceptions; @@ -167,6 +159,23 @@ namespace v2rayN.Handler // config.remoteDNS = "1.1.1.1"; //} + if (config.speedTestItem == null) + { + config.speedTestItem = new(); + } + if (config.speedTestItem.speedTestTimeout < 10) + { + config.speedTestItem.speedTestTimeout = 10; + } + if (Utils.IsNullOrEmpty(config.speedTestItem.speedTestUrl)) + { + config.speedTestItem.speedTestUrl = Global.SpeedTestUrls[0]; + } + if (Utils.IsNullOrEmpty(config.speedTestItem.speedPingTestUrl)) + { + config.speedTestItem.speedPingTestUrl = Global.SpeedPingTestUrl; + } + if (config.statisticsFreshRate > 100 || config.statisticsFreshRate < 1) { config.statisticsFreshRate = 1; diff --git a/v2rayN/v2rayN/Handler/DownloadHandle.cs b/v2rayN/v2rayN/Handler/DownloadHandle.cs index a3fcfbdd..bbba51e5 100644 --- a/v2rayN/v2rayN/Handler/DownloadHandle.cs +++ b/v2rayN/v2rayN/Handler/DownloadHandle.cs @@ -33,20 +33,13 @@ namespace v2rayN.Handler public async Task DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Action update) { - var hasValue = false; try { Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().enableSecurityProtocolTls13); - var client = new HttpClient(new SocketsHttpHandler() - { - Proxy = webProxy - }); - var progress = new Progress(); progress.ProgressChanged += (sender, value) => { - hasValue = true; if (update != null) { string msg = $"{value}"; @@ -54,22 +47,17 @@ namespace v2rayN.Handler } }; - var cancellationToken = new CancellationTokenSource(); - cancellationToken.CancelAfter(downloadTimeout * 1000); - await HttpClientHelper.GetInstance().DownloadDataAsync4Speed(client, + await DownloaderHelper.Instance.DownloadDataAsync4Speed(webProxy, url, progress, - cancellationToken.Token); + downloadTimeout); } catch (Exception ex) { - if (!hasValue) + update(false, ex.Message); + if (ex.InnerException != null) { - update(false, ex.Message); - if (ex.InnerException != null) - { - update(false, ex.InnerException.Message); - } + update(false, ex.InnerException.Message); } } return 0; @@ -196,7 +184,7 @@ namespace v2rayN.Handler try { var config = LazyConfig.Instance.GetConfig(); - string status = GetRealPingTime(config.constItem.speedPingTestUrl, webProxy, 10, out int responseTime); + string status = GetRealPingTime(config.speedTestItem.speedPingTestUrl, webProxy, 10, out int responseTime); bool noError = Utils.IsNullOrEmpty(status); return noError ? responseTime : -1; } diff --git a/v2rayN/v2rayN/Handler/SpeedtestHandler.cs b/v2rayN/v2rayN/Handler/SpeedtestHandler.cs index 1de7f492..98ea503f 100644 --- a/v2rayN/v2rayN/Handler/SpeedtestHandler.cs +++ b/v2rayN/v2rayN/Handler/SpeedtestHandler.cs @@ -195,10 +195,10 @@ namespace v2rayN.Handler private async Task RunSpeedTestAsync() { int pid = -1; - if (_actionType == ESpeedActionType.Mixedtest) - { - _selecteds = _selecteds.OrderBy(t => t.delay).ToList(); - } + //if (_actionType == ESpeedActionType.Mixedtest) + //{ + // _selecteds = _selecteds.OrderBy(t => t.delay).ToList(); + //} pid = _coreHandler.LoadCoreConfigString(_config, _selecteds); if (pid < 0) @@ -207,10 +207,11 @@ namespace v2rayN.Handler return; } - string url = _config.constItem.speedTestUrl; + string url = _config.speedTestItem.speedTestUrl; + var timeout = _config.speedTestItem.speedTestTimeout; + DownloadHandle downloadHandle = new DownloadHandle(); - var timeout = 8; foreach (var it in _selecteds) { if (!it.allowTest) @@ -221,11 +222,11 @@ namespace v2rayN.Handler { continue; } - if (it.delay < 0) - { - UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip); - continue; - } + //if (it.delay < 0) + //{ + // UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip); + // continue; + //} _ = LazyConfig.Instance.SetTestResult(it.indexId, "", "-1"); var item = LazyConfig.Instance.GetProfileItem(it.indexId); @@ -250,18 +251,71 @@ namespace v2rayN.Handler } UpdateFunc("", ResUI.SpeedtestingCompleted); } + + private async Task RunSpeedTestMulti() + { + int pid = -1; + pid = _coreHandler.LoadCoreConfigString(_config, _selecteds); + if (pid < 0) + { + UpdateFunc("", ResUI.FailedToRunCore); + return; + } + + string url = _config.speedTestItem.speedTestUrl; + var timeout = _config.speedTestItem.speedTestTimeout; + + DownloadHandle downloadHandle = new DownloadHandle(); + + foreach (var it in _selecteds) + { + if (!it.allowTest) + { + continue; + } + if (it.configType == EConfigType.Custom) + { + continue; + } + _ = LazyConfig.Instance.SetTestResult(it.indexId, "", "-1"); + + var item = LazyConfig.Instance.GetProfileItem(it.indexId); + if (item is null) continue; + + WebProxy webProxy = new WebProxy(Global.Loopback, it.port); + _ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (bool success, string msg) => + { + decimal.TryParse(msg, out decimal dec); + if (dec > 0) + { + _ = LazyConfig.Instance.SetTestResult(it.indexId, "", msg); + } + UpdateFunc(it.indexId, "", msg); + }); + Thread.Sleep(2000); + } + + Thread.Sleep((timeout + 2) * 1000); + + if (pid > 0) + { + _coreHandler.CoreStopPid(pid); + } + UpdateFunc("", ResUI.SpeedtestingCompleted); + } + private async Task RunMixedtestAsync() { await RunRealPing(); Thread.Sleep(1000); - await RunSpeedTestAsync(); + await RunSpeedTestMulti(); } public string GetRealPingTime(DownloadHandle downloadHandle, WebProxy webProxy) { - string status = downloadHandle.GetRealPingTime(_config.constItem.speedPingTestUrl, webProxy, 10, out int responseTime); + string status = downloadHandle.GetRealPingTime(_config.speedTestItem.speedPingTestUrl, webProxy, 10, out int responseTime); //string output = Utils.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status; return FormatOut(Utils.IsNullOrEmpty(status) ? responseTime : -1, Global.DelayUnit); } diff --git a/v2rayN/v2rayN/Handler/TunHandler.cs b/v2rayN/v2rayN/Handler/TunHandler.cs index 591af86c..ef0e99ff 100644 --- a/v2rayN/v2rayN/Handler/TunHandler.cs +++ b/v2rayN/v2rayN/Handler/TunHandler.cs @@ -184,12 +184,12 @@ namespace v2rayN.Base configStr = configStr.Replace("$ruleProxyIPs$", ""); configStr = configStr.Replace("$ruleProxyProcess$", ""); configStr = configStr.Replace("$ruleFinally$", ""); - + File.WriteAllText(Utils.GetConfigPath(_tunConfigName), configStr); return true; - } + } private void CoreStop() { diff --git a/v2rayN/v2rayN/Mode/Config.cs b/v2rayN/v2rayN/Mode/Config.cs index e407ab33..cfcc9668 100644 --- a/v2rayN/v2rayN/Mode/Config.cs +++ b/v2rayN/v2rayN/Mode/Config.cs @@ -152,6 +152,7 @@ public GrpcItem grpcItem { get; set; } public UIItem uiItem { get; set; } public ConstItem constItem { get; set; } + public SpeedTestItem speedTestItem { get; set; } public List inbound { get; set; } public List globalHotkeys { get; set; } public List coreTypeItem { get; set; } diff --git a/v2rayN/v2rayN/Mode/ConfigItems.cs b/v2rayN/v2rayN/Mode/ConfigItems.cs index 0bce7632..f652aad0 100644 --- a/v2rayN/v2rayN/Mode/ConfigItems.cs +++ b/v2rayN/v2rayN/Mode/ConfigItems.cs @@ -73,8 +73,6 @@ namespace v2rayN.Mode [Serializable] public class ConstItem { - public string speedTestUrl { get; set; } - public string speedPingTestUrl { get; set; } public string defIEProxyExceptions { get; set; } } @@ -117,4 +115,14 @@ namespace v2rayN.Mode public List proxyProcess { get; set; } } + + [Serializable] + public class SpeedTestItem + { + public int speedTestTimeout { get; set; } + public string speedTestUrl { get; set; } + public string speedPingTestUrl { get; set; } + + } + } diff --git a/v2rayN/v2rayN/Resx/ResUI.Designer.cs b/v2rayN/v2rayN/Resx/ResUI.Designer.cs index ffddd60f..43e66e4c 100644 --- a/v2rayN/v2rayN/Resx/ResUI.Designer.cs +++ b/v2rayN/v2rayN/Resx/ResUI.Designer.cs @@ -790,7 +790,7 @@ namespace v2rayN.Resx { } /// - /// 查找类似 One-click test Latency and speed (Ctrl+E) 的本地化字符串。 + /// 查找类似 One-click multi test Latency and speed (Ctrl+E) 的本地化字符串。 /// public static string menuMixedTestServer { get { @@ -2689,6 +2689,24 @@ namespace v2rayN.Resx { } } + /// + /// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。 + /// + public static string TbSettingsSpeedTestTimeout { + get { + return ResourceManager.GetString("TbSettingsSpeedTestTimeout", resourceCulture); + } + } + + /// + /// 查找类似 SpeedTest Url 的本地化字符串。 + /// + public static string TbSettingsSpeedTestUrl { + get { + return ResourceManager.GetString("TbSettingsSpeedTestUrl", resourceCulture); + } + } + /// /// 查找类似 Start on boot 的本地化字符串。 /// diff --git a/v2rayN/v2rayN/Resx/ResUI.resx b/v2rayN/v2rayN/Resx/ResUI.resx index 91a5abbd..95cd3332 100644 --- a/v2rayN/v2rayN/Resx/ResUI.resx +++ b/v2rayN/v2rayN/Resx/ResUI.resx @@ -1022,7 +1022,7 @@ RouteOnly - One-click test Latency and speed (Ctrl+E) + One-click multi test Latency and speed (Ctrl+E) Delay(ms) @@ -1120,4 +1120,10 @@ Enable: If no route matches, the final proxy + + SpeedTest Single Timeout Value + + + SpeedTest Url + \ No newline at end of file diff --git a/v2rayN/v2rayN/Resx/ResUI.zh-Hans.resx b/v2rayN/v2rayN/Resx/ResUI.zh-Hans.resx index a02aaddf..07f4c454 100644 --- a/v2rayN/v2rayN/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/v2rayN/Resx/ResUI.zh-Hans.resx @@ -1022,7 +1022,7 @@ RouteOnly - 一键测试延迟和速度 (Ctrl+E) + 一键多线程测试延迟和速度 (Ctrl+E) 延迟(ms) @@ -1120,4 +1120,10 @@ 启用:路由无匹配则最终代理 + + 测速单个超时值 + + + 测速文件地址 + \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs b/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs index 1fa90ba0..5d2cee28 100644 --- a/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs @@ -1225,7 +1225,7 @@ namespace v2rayN.ViewModels } private void UpdateSubscriptionProcess(string subId, bool blProxy) - { + { (new UpdateHandle()).UpdateSubscriptionProcess(_config, subId, blProxy, UpdateTaskHandler); } diff --git a/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs b/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs index 756eacbe..461caa0b 100644 --- a/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs @@ -63,6 +63,8 @@ namespace v2rayN.ViewModels [Reactive] public int autoUpdateSubInterval { get; set; } [Reactive] public int trayMenuServersLimit { get; set; } [Reactive] public string currentFontFamily { get; set; } + [Reactive] public int SpeedTestTimeout { get; set; } + [Reactive] public string SpeedTestUrl { get; set; } #endregion @@ -153,6 +155,8 @@ namespace v2rayN.ViewModels autoUpdateSubInterval = _config.autoUpdateSubInterval; trayMenuServersLimit = _config.trayMenuServersLimit; currentFontFamily = _config.uiItem.currentFontFamily; + SpeedTestTimeout = _config.speedTestItem.speedTestTimeout; + SpeedTestUrl = _config.speedTestItem.speedTestUrl; #endregion @@ -327,6 +331,8 @@ namespace v2rayN.ViewModels _config.uiItem.doubleClick2Activate = DoubleClick2Activate; _config.trayMenuServersLimit = trayMenuServersLimit; _config.uiItem.currentFontFamily = currentFontFamily; + _config.speedTestItem.speedTestTimeout = SpeedTestTimeout; + _config.speedTestItem.speedTestUrl = SpeedTestUrl; //systemProxy _config.systemProxyExceptions = systemProxyExceptions; diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 14e76d05..ffd935e3 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -478,6 +478,8 @@ + + @@ -660,6 +662,7 @@ Grid.Column="1" Width="200" Margin="{StaticResource SettingItemMargin}" + HorizontalAlignment="Left" Style="{StaticResource DefTextBox}" /> + + + + + + diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs index 9fc093ed..0d35bed9 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs @@ -63,12 +63,21 @@ namespace v2rayN.Views cmbCoreType6.Items.Add(it); }); + for (int i = 2; i <= 6; i++) + { + cmbSpeedTestTimeout.Items.Add(i * 5); + } + Global.SpeedTestUrls.ForEach(it => + { + cmbSpeedTestUrl.Items.Add(it); + }); + //fill fonts try { var dir = new DirectoryInfo(Utils.GetFontsPath()); var files = dir.GetFiles("*.ttf"); - var culture = _config.uiItem.currentLanguage.Equals(Global.Languages[0]) ? "zh-cn" : "en-us"; + var culture = _config.uiItem.currentLanguage.Equals(Global.Languages[0]) ? "zh-cn" : "en-us"; var culture2 = "en-us"; foreach (var it in files) { @@ -153,6 +162,8 @@ namespace v2rayN.Views this.Bind(ViewModel, vm => vm.autoUpdateSubInterval, v => v.txtautoUpdateSubInterval.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.trayMenuServersLimit, v => v.txttrayMenuServersLimit.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.currentFontFamily, v => v.cmbcurrentFontFamily.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/v2rayN.csproj b/v2rayN/v2rayN/v2rayN.csproj index 3a6654d5..632d4853 100644 --- a/v2rayN/v2rayN/v2rayN.csproj +++ b/v2rayN/v2rayN/v2rayN.csproj @@ -13,6 +13,7 @@ + @@ -22,10 +23,10 @@ - - - - + + + +