diff --git a/Netch/Controllers/DNSController.cs b/Netch/Controllers/DNSController.cs index 4c2aaad3..835d1714 100644 --- a/Netch/Controllers/DNSController.cs +++ b/Netch/Controllers/DNSController.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading.Tasks; using Netch.Interfaces; using static Netch.Interops.AioDNS; @@ -9,9 +10,9 @@ namespace Netch.Controllers { public string Name => "DNS Service"; - public void Stop() + public async Task StopAsync() { - Free(); + await FreeAsync(); } public void Start() diff --git a/Netch/Controllers/Guard.cs b/Netch/Controllers/Guard.cs index 91be8ebb..f86022a5 100644 --- a/Netch/Controllers/Guard.cs +++ b/Netch/Controllers/Guard.cs @@ -4,8 +4,8 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Text; -using System.Threading; using System.Threading.Tasks; +using Microsoft.VisualStudio.Threading; using Netch.Models; using Netch.Utils; using Serilog; @@ -67,7 +67,7 @@ namespace Netch.Controllers Instance.Dispose(); } - protected void StartGuard(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal) + protected async Task StartGuardAsync(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal) { State = State.Starting; @@ -82,8 +82,8 @@ namespace Netch.Controllers if (RedirectOutput) { - Task.Run(() => ReadOutput(Instance.StandardOutput)); - Task.Run(() => ReadOutput(Instance.StandardError)); + Task.Run(() => ReadOutput(Instance.StandardOutput)).Forget(); + Task.Run(() => ReadOutput(Instance.StandardError)).Forget(); if (!StartedKeywords.Any()) { @@ -95,20 +95,20 @@ namespace Netch.Controllers // wait ReadOutput change State for (var i = 0; i < 1000; i++) { - Thread.Sleep(10); + await Task.Delay(50); switch (State) { case State.Started: OnStarted(); return; case State.Stopped: - StopGuard(); + await StopGuardAsync(); OnStartFailed(); throw new MessageException($"{Name} 控制器启动失败"); } } - StopGuard(); + await StopGuardAsync(); throw new MessageException($"{Name} 控制器启动超时"); } } @@ -136,12 +136,12 @@ namespace Netch.Controllers State = State.Stopped; } - public virtual void Stop() + public virtual async Task StopAsync() { - StopGuard(); + await StopGuardAsync(); } - protected void StopGuard() + protected async Task StopGuardAsync() { _logStreamWriter?.Close(); _logFileStream?.Close(); @@ -151,7 +151,7 @@ namespace Netch.Controllers if (Instance is { HasExited: false }) { Instance.Kill(); - Instance.WaitForExit(); + await Instance.WaitForExitAsync(); } } catch (Win32Exception e) diff --git a/Netch/Controllers/MainController.cs b/Netch/Controllers/MainController.cs index 3e48e62c..6b88cf7e 100644 --- a/Netch/Controllers/MainController.cs +++ b/Netch/Controllers/MainController.cs @@ -24,7 +24,7 @@ namespace Netch.Controllers { Log.Information("启动主控制器: {Server} {Mode}", $"{server.Type}", $"[{(int) mode.Type}]{mode.Remark}"); - if (DnsUtils.Lookup(server.Hostname) == null) + if (await DnsUtils.LookupAsync(server.Hostname) == null) throw new MessageException(i18N.Translate("Lookup Server hostname failed")); Mode = mode; @@ -45,9 +45,9 @@ namespace Netch.Controllers try { if (!ModeHelper.SkipServerController(server, mode)) - server = await Task.Run(() => StartServer(server)); + server = await StartServerAsync(server); - await Task.Run(() => StartMode(server, mode)); + await StartModeAsync(server, mode); } catch (Exception e) { @@ -67,7 +67,7 @@ namespace Netch.Controllers } } - private static Server StartServer(Server server) + private static async Task StartServerAsync(Server server) { ServerController = ServerHelper.GetUtilByTypeName(server.Type).GetController(); @@ -76,7 +76,7 @@ namespace Netch.Controllers Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ServerController.Name)); Log.Debug("Server Information: {Data}", $"{server.Type} {server.MaskedData()}"); - var socks5 = ServerController.Start(server); + var socks5 = await ServerController.StartAsync(server); StatusPortInfoText.Socks5Port = socks5.Port; StatusPortInfoText.UpdateShareLan(); @@ -84,7 +84,7 @@ namespace Netch.Controllers return socks5; } - private static void StartMode(Server server, Mode mode) + private static async Task StartModeAsync(Server server, Mode mode) { ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName); @@ -93,7 +93,7 @@ namespace Netch.Controllers Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name)); - ModeController.Start(server, mode); + await ModeController.StartAsync(server, mode); } public static async Task StopAsync() @@ -103,12 +103,12 @@ namespace Netch.Controllers StatusPortInfoText.Reset(); - Task.Run(() => NTTController.Stop()).Forget(); + Task.Run(() => NTTController.StopAsync()).Forget(); var tasks = new[] { - Task.Run(() => ServerController?.Stop()), - Task.Run(() => ModeController?.Stop()) + Task.Run(() => ServerController?.StopAsync()), + Task.Run(() => ModeController?.StopAsync()) }; try diff --git a/Netch/Controllers/NFController.cs b/Netch/Controllers/NFController.cs index 295bc851..b51df257 100644 --- a/Netch/Controllers/NFController.cs +++ b/Netch/Controllers/NFController.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.ServiceProcess; +using System.Threading.Tasks; using Netch.Interfaces; using Netch.Interops; using Netch.Models; @@ -27,7 +28,7 @@ namespace Netch.Controllers public string Name => "Redirector"; - public void Start(Server server, Mode mode) + public async Task StartAsync(Server server, Mode mode) { _server = server; _mode = mode; @@ -43,7 +44,7 @@ namespace Netch.Controllers // Server Dial(NameList.TYPE_FILTERUDP, _rdrConfig.FilterProtocol.HasFlag(PortType.UDP).ToString().ToLower()); Dial(NameList.TYPE_FILTERTCP, _rdrConfig.FilterProtocol.HasFlag(PortType.TCP).ToString().ToLower()); - dial_Server(_rdrConfig.FilterProtocol, _server); + await DialServerAsync(_rdrConfig.FilterProtocol, _server); // Mode Rule dial_Name(_mode); @@ -51,13 +52,13 @@ namespace Netch.Controllers // Features Dial(NameList.TYPE_DNSHOST, _rdrConfig.DNSHijack ? _rdrConfig.DNSHijackHost : ""); - if (!Init()) + if (!await InitAsync()) throw new MessageException("Redirector start failed."); } - public void Stop() + public async Task StopAsync() { - Free(); + await FreeAsync(); } #region CheckRule @@ -102,12 +103,12 @@ namespace Netch.Controllers #endregion - private void dial_Server(PortType portType, in Server server) + private async Task DialServerAsync(PortType portType, Server server) { if (portType == PortType.Both) { - dial_Server(PortType.TCP, server); - dial_Server(PortType.UDP, server); + await DialServerAsync(PortType.TCP, server); + await DialServerAsync(PortType.UDP, server); return; } @@ -116,7 +117,7 @@ namespace Netch.Controllers if (server is Socks5 socks5) { Dial(NameList.TYPE_TCPTYPE + offset, "Socks5"); - Dial(NameList.TYPE_TCPHOST + offset, $"{socks5.AutoResolveHostname()}:{socks5.Port}"); + Dial(NameList.TYPE_TCPHOST + offset, $"{await socks5.AutoResolveHostnameAsync()}:{socks5.Port}"); Dial(NameList.TYPE_TCPUSER + offset, socks5.Username ?? string.Empty); Dial(NameList.TYPE_TCPPASS + offset, socks5.Password ?? string.Empty); Dial(NameList.TYPE_TCPMETH + offset, string.Empty); @@ -124,7 +125,7 @@ namespace Netch.Controllers else if (server is Shadowsocks shadowsocks && !shadowsocks.HasPlugin() && _rdrConfig.RedirectorSS) { Dial(NameList.TYPE_TCPTYPE + offset, "Shadowsocks"); - Dial(NameList.TYPE_TCPHOST + offset, $"{shadowsocks.AutoResolveHostname()}:{shadowsocks.Port}"); + Dial(NameList.TYPE_TCPHOST + offset, $"{await shadowsocks.AutoResolveHostnameAsync()}:{shadowsocks.Port}"); Dial(NameList.TYPE_TCPMETH + offset, shadowsocks.EncryptMethod); Dial(NameList.TYPE_TCPPASS + offset, shadowsocks.Password); } diff --git a/Netch/Controllers/NTTController.cs b/Netch/Controllers/NTTController.cs index 96f49660..5a093f35 100644 --- a/Netch/Controllers/NTTController.cs +++ b/Netch/Controllers/NTTController.cs @@ -20,7 +20,7 @@ namespace Netch.Controllers /// 启动 NatTypeTester /// /// - public async Task<(string? result, string? localEnd, string? publicEnd)> Start() + public async Task<(string? result, string? localEnd, string? publicEnd)> StartAsync() { string? localEnd = null, publicEnd = null, result = null, bindingTest = null; @@ -87,7 +87,7 @@ namespace Netch.Controllers Log.Error(e, "{Name} 控制器启动异常", Name); try { - Stop(); + await StopAsync(); } catch { diff --git a/Netch/Controllers/PcapController.cs b/Netch/Controllers/PcapController.cs index ef1a27b0..ec6f1239 100644 --- a/Netch/Controllers/PcapController.cs +++ b/Netch/Controllers/PcapController.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.VisualStudio.Threading; using Netch.Forms; using Netch.Interfaces; using Netch.Models; @@ -29,7 +30,7 @@ namespace Netch.Controllers public override string Name => "pcap2socks"; - public void Start(Server server, Mode mode) + public async Task StartAsync(Server server, Mode mode) { _server = server; _mode = mode; @@ -38,18 +39,18 @@ namespace Netch.Controllers var argument = new StringBuilder($@"-i \Device\NPF_{outboundNetworkInterface.Id}"); if (_server is Socks5 socks5 && !socks5.Auth()) - argument.Append($" --destination {socks5.AutoResolveHostname()}:{socks5.Port}"); + argument.Append($" --destination {await socks5.AutoResolveHostnameAsync()}:{socks5.Port}"); else argument.Append($" --destination 127.0.0.1:{Global.Settings.Socks5LocalPort}"); argument.Append($" {_mode.GetRules().FirstOrDefault() ?? "-P n"}"); - StartGuard(argument.ToString()); + await StartGuardAsync(argument.ToString()); } - public override void Stop() + public override async Task StopAsync() { Global.MainForm.Invoke(new Action(() => { _form.Close(); })); - StopGuard(); + await StopGuardAsync(); } ~PcapController() @@ -79,7 +80,7 @@ namespace Netch.Controllers { Thread.Sleep(1000); Utils.Utils.Open("https://github.com/zhxie/pcap2socks#dependencies"); - }); + }).Forget(); throw new MessageException("Pleases install pcap2socks's dependency"); } diff --git a/Netch/Controllers/TUNController.cs b/Netch/Controllers/TUNController.cs index fe0afb4a..9e7c87fe 100644 --- a/Netch/Controllers/TUNController.cs +++ b/Netch/Controllers/TUNController.cs @@ -30,13 +30,13 @@ namespace Netch.Controllers public string Name => "tun2socks"; - public void Start(Server server, Mode mode) + public async Task StartAsync(Server server, Mode mode) { _mode = mode; _tunConfig = Global.Settings.TUNTAP; if (server is Socks5Bridge socks5Bridge) - _serverRemoteAddress = DnsUtils.Lookup(socks5Bridge.RemoteHostname); + _serverRemoteAddress = await DnsUtils.LookupAsync(socks5Bridge.RemoteHostname); if (_serverRemoteAddress != null && IPAddress.IsLoopback(_serverRemoteAddress)) _serverRemoteAddress = null; @@ -58,9 +58,9 @@ namespace Netch.Controllers if (server is Socks5 socks5) { - Dial(NameList.TYPE_TCPHOST, $"{socks5.AutoResolveHostname()}:{socks5.Port}"); + Dial(NameList.TYPE_TCPHOST, $"{await socks5.AutoResolveHostnameAsync()}:{socks5.Port}"); - Dial(NameList.TYPE_UDPHOST, $"{socks5.AutoResolveHostname()}:{socks5.Port}"); + Dial(NameList.TYPE_UDPHOST, $"{await socks5.AutoResolveHostnameAsync()}:{socks5.Port}"); if (socks5.Auth()) { @@ -106,16 +106,16 @@ namespace Netch.Controllers SetupRouteTable(); } - public void Stop() + public async Task StopAsync() { var tasks = new[] { - Task.Run(Free), + FreeAsync(), Task.Run(ClearRouteTable), - Task.Run(_aioDnsController.Stop) + _aioDnsController.StopAsync() }; - Task.WaitAll(tasks); + await Task.WhenAll(tasks); } private void CheckDriver() diff --git a/Netch/Controllers/UpdateChecker.cs b/Netch/Controllers/UpdateChecker.cs index 5c673c7d..faf583f8 100644 --- a/Netch/Controllers/UpdateChecker.cs +++ b/Netch/Controllers/UpdateChecker.cs @@ -37,14 +37,14 @@ namespace Netch.Controllers public static event EventHandler? NewVersionNotFound; - public static async Task Check(bool isPreRelease) + public static async Task CheckAsync(bool isPreRelease) { try { var updater = new GitHubRelease(Owner, Repo); var url = updater.AllReleaseUrl; - var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url)); + var (_, json) = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url)); var releases = JsonSerializer.Deserialize>(json)!; LatestRelease = GetLatestRelease(releases, isPreRelease); @@ -52,12 +52,12 @@ namespace Netch.Controllers if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0) { Log.Information("发现新版本"); - NewVersionFound?.Invoke(null, new EventArgs()); + NewVersionFound?.Invoke(null, EventArgs.Empty); } else { Log.Information("目前是最新版本"); - NewVersionNotFound?.Invoke(null, new EventArgs()); + NewVersionNotFound?.Invoke(null, EventArgs.Empty); } } catch (Exception e) diff --git a/Netch/Forms/MainForm.cs b/Netch/Forms/MainForm.cs index 8b3026cd..3dab0923 100644 --- a/Netch/Forms/MainForm.cs +++ b/Netch/Forms/MainForm.cs @@ -93,23 +93,17 @@ namespace Netch.Forms // 加载快速配置 LoadProfiles(); - BeginInvoke(new Action(async () => - { - // 检查更新 - if (Global.Settings.CheckUpdateWhenOpened) - await CheckUpdate(); - })); + // 检查更新 + if (Global.Settings.CheckUpdateWhenOpened) + CheckUpdateAsync().Forget(); - BeginInvoke(new Action(async () => - { - // 检查订阅更新 - if (Global.Settings.UpdateServersWhenOpened) - await UpdateServersFromSubscribe(); + // 检查订阅更新 + if (Global.Settings.UpdateServersWhenOpened) + UpdateServersFromSubscribeAsync().Forget(); - // 打开软件时启动加速,产生开始按钮点击事件 - if (Global.Settings.StartWhenOpened) - ControlButton_Click(null, null); - })); + // 打开软件时启动加速,产生开始按钮点击事件 + if (Global.Settings.StartWhenOpened) + ControlButton.PerformClick(); Netch.SingleInstance.ListenForArgumentsFromSuccessiveInstances(); } @@ -275,10 +269,10 @@ namespace Netch.Forms private async void UpdateServersFromSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e) { - await UpdateServersFromSubscribe(); + await UpdateServersFromSubscribeAsync(); } - private async Task UpdateServersFromSubscribe() + private async Task UpdateServersFromSubscribeAsync() { void DisableItems(bool v) { @@ -333,7 +327,7 @@ namespace Netch.Forms { UpdateChecker.NewVersionNotFound += OnNewVersionNotFound; UpdateChecker.NewVersionFoundFailed += OnNewVersionFoundFailed; - await CheckUpdate(); + await CheckUpdateAsync(); } finally { @@ -394,7 +388,6 @@ namespace Netch.Forms private void ShowHideConsoleToolStripMenuItem_Click(object sender, EventArgs e) { - var windowStyles = (WINDOW_STYLE)PInvoke.GetWindowLong(new HWND(Netch.ConsoleHwnd), WINDOW_LONG_PTR_INDEX.GWL_STYLE); var visible = windowStyles.HasFlag(WINDOW_STYLE.WS_VISIBLE); PInvoke.ShowWindow(Netch.ConsoleHwnd, visible ? SHOW_WINDOW_CMD.SW_HIDE : SHOW_WINDOW_CMD.SW_SHOWNOACTIVATE); @@ -468,7 +461,7 @@ namespace Netch.Forms } ModeHelper.SuspendWatcher = true; - await Stop(); + await StopAsync(); await Configuration.SaveAsync(); // Update @@ -515,7 +508,7 @@ namespace Netch.Forms { if (!IsWaiting()) { - await StopCore(); + await StopCoreAsync(); return; } @@ -551,28 +544,31 @@ namespace Netch.Forms State = State.Started; Task.Run(Bandwidth.NetTraffic).Forget(); - Task.Run(NatTest).Forget(); + NatTestAsync().Forget(); if (Global.Settings.MinimizeWhenStarted) Minimize(); // 自动检测延迟 - Task.Run(() => + async Task StartedPingAsync() + { + while (State == State.Started) { - while (State == State.Started) - if (Global.Settings.StartedPingInterval >= 0) - { - server.Test(); - ServerComboBox.Refresh(); + if (Global.Settings.StartedPingInterval >= 0) + { + await server.PingAsync(); + ServerComboBox.Refresh(); - Thread.Sleep(Global.Settings.StartedPingInterval * 1000); - } - else - { - Thread.Sleep(5000); - } - }) - .Forget(); + await Task.Delay(Global.Settings.StartedPingInterval * 1000); + } + else + { + await Task.Delay(5000); + } + } + } + + StartedPingAsync().Forget(); } #endregion @@ -650,7 +646,7 @@ namespace Netch.Forms Show(); } - private void SpeedPictureBox_Click(object sender, EventArgs e) + private async void SpeedPictureBox_Click(object sender, EventArgs e) { void Enable() { @@ -664,19 +660,13 @@ namespace Netch.Forms if (!IsWaiting() || ModifierKeys == Keys.Control) { - (ServerComboBox.SelectedItem as Server)?.Test(); + (ServerComboBox.SelectedItem as Server)?.PingAsync(); Enable(); } else { - ServerHelper.DelayTestHelper.TestDelayFinished += OnTestDelayFinished; - Task.Run(ServerHelper.DelayTestHelper.TestAllDelay).Forget(); - - void OnTestDelayFinished(object? o1, EventArgs? e1) - { - ServerHelper.DelayTestHelper.TestDelayFinished -= OnTestDelayFinished; - Enable(); - } + await ServerHelper.DelayTestHelper.TestAllDelayAsync(); + Enable(); } } @@ -1057,15 +1047,15 @@ namespace Netch.Forms } } - public async Task Stop() + public async Task StopAsync() { if (IsWaiting()) return; - await StopCore(); + await StopCoreAsync(); } - private async Task StopCore() + private async Task StopCoreAsync() { State = State.Stopping; await MainController.StopAsync(); @@ -1172,7 +1162,7 @@ namespace Netch.Forms private async void NatTypeStatusLabel_Click(object sender, EventArgs e) { if (_state == State.Started && !Monitor.IsEntered(_natTestLock)) - await NatTest(); + await NatTestAsync(); } private bool _natTestLock = true; @@ -1180,7 +1170,7 @@ namespace Netch.Forms /// /// 测试 NAT /// - private async Task NatTest() + private async Task NatTestAsync() { if (!MainController.Mode!.TestNatRequired()) return; @@ -1194,11 +1184,11 @@ namespace Netch.Forms { NatTypeStatusText(i18N.Translate("Testing NAT")); - var (result, _, publicEnd) = await MainController.NTTController.Start(); + var (result, _, publicEnd) = await MainController.NTTController.StartAsync(); if (!string.IsNullOrEmpty(publicEnd)) { - var country = Utils.Utils.GetCityCode(publicEnd!); + var country = await Utils.Utils.GetCityCodeAsync(publicEnd!); NatTypeStatusText(result, country); } else @@ -1285,7 +1275,7 @@ namespace Netch.Forms if (File.Exists(file)) File.Delete(file); - await Stop(); + await StopAsync(); Dispose(); Environment.Exit(Environment.ExitCode); @@ -1315,12 +1305,12 @@ namespace Netch.Forms #region Updater - private async Task CheckUpdate() + private async Task CheckUpdateAsync() { try { UpdateChecker.NewVersionFound += OnUpdateCheckerOnNewVersionFound; - await UpdateChecker.Check(Global.Settings.CheckBetaUpdate); + await UpdateChecker.CheckAsync(Global.Settings.CheckBetaUpdate); if (Flags.AlwaysShowNewVersionFound) OnUpdateCheckerOnNewVersionFound(null!, null!); } diff --git a/Netch/Interfaces/IController.cs b/Netch/Interfaces/IController.cs index a7837ec2..80cb340e 100644 --- a/Netch/Interfaces/IController.cs +++ b/Netch/Interfaces/IController.cs @@ -1,9 +1,11 @@ -namespace Netch.Interfaces +using System.Threading.Tasks; + +namespace Netch.Interfaces { public interface IController { public string Name { get; } - public void Stop(); + public Task StopAsync(); } } \ No newline at end of file diff --git a/Netch/Interfaces/IModeController.cs b/Netch/Interfaces/IModeController.cs index b0d5f960..4577c67e 100644 --- a/Netch/Interfaces/IModeController.cs +++ b/Netch/Interfaces/IModeController.cs @@ -1,9 +1,10 @@ +using System.Threading.Tasks; using Netch.Models; namespace Netch.Interfaces { public interface IModeController : IController { - public void Start(Server server, Mode mode); + public Task StartAsync(Server server, Mode mode); } } \ No newline at end of file diff --git a/Netch/Interfaces/IServerController.cs b/Netch/Interfaces/IServerController.cs index 48676a5b..d955292f 100644 --- a/Netch/Interfaces/IServerController.cs +++ b/Netch/Interfaces/IServerController.cs @@ -1,4 +1,5 @@ -using Netch.Models; +using System.Threading.Tasks; +using Netch.Models; using Netch.Servers; namespace Netch.Interfaces @@ -9,7 +10,7 @@ namespace Netch.Interfaces public string? LocalAddress { get; set; } - public Socks5 Start(in Server s); + public Task StartAsync(Server s); } public static class ServerControllerExtension diff --git a/Netch/Interops/AioDNS.cs b/Netch/Interops/AioDNS.cs index 1d5624ac..a81368db 100644 --- a/Netch/Interops/AioDNS.cs +++ b/Netch/Interops/AioDNS.cs @@ -1,5 +1,6 @@ using System.Runtime.InteropServices; using System.Text; +using System.Threading.Tasks; using Serilog; namespace Netch.Interops @@ -14,6 +15,11 @@ namespace Netch.Interops return aiodns_dial(name, Encoding.UTF8.GetBytes(value)); } + public static async Task FreeAsync() + { + await Task.Run(Free).ConfigureAwait(false); + } + [DllImport(aiodns_bin, CallingConvention = CallingConvention.Cdecl)] private static extern bool aiodns_dial(NameList name, byte[] value); diff --git a/Netch/Interops/Redirector.cs b/Netch/Interops/Redirector.cs index db15047a..8c06f89d 100644 --- a/Netch/Interops/Redirector.cs +++ b/Netch/Interops/Redirector.cs @@ -1,4 +1,5 @@ using System.Runtime.InteropServices; +using System.Threading.Tasks; using Serilog; namespace Netch.Interops @@ -47,14 +48,15 @@ namespace Netch.Interops return aio_dial(name, value); } - public static bool Init() + public static async Task InitAsync() { - return aio_init(); + return await Task.Run(aio_init).ConfigureAwait(false); } - public static bool Free() + public static async Task FreeAsync() { - return aio_free(); + + return await Task.Run(aio_free).ConfigureAwait(false); } public const int UdpNameListOffset = (int)NameList.TYPE_UDPLISN - (int)NameList.TYPE_TCPLISN; diff --git a/Netch/Interops/tun2socks.cs b/Netch/Interops/tun2socks.cs index 8280cda8..4d15a52e 100644 --- a/Netch/Interops/tun2socks.cs +++ b/Netch/Interops/tun2socks.cs @@ -1,5 +1,6 @@ using System.Runtime.InteropServices; using System.Text; +using System.Threading.Tasks; using Serilog; namespace Netch.Interops @@ -46,9 +47,9 @@ namespace Netch.Interops return tun_init(); } - public static bool Free() + public static async Task FreeAsync() { - return tun_free(); + return await Task.Run(tun_free).ConfigureAwait(false); } private const string tun2socks_bin = "tun2socks.bin"; diff --git a/Netch/Models/Server.cs b/Netch/Models/Server.cs index c11cdd2d..fd793b7c 100644 --- a/Netch/Models/Server.cs +++ b/Netch/Models/Server.cs @@ -48,7 +48,6 @@ namespace Netch.Models // ReSharper disable once CollectionNeverUpdated.Global public Dictionary ExtensionData { get; set; } = new(); - public object Clone() { return MemberwiseClone(); @@ -68,39 +67,42 @@ namespace Netch.Models } public abstract string MaskedData(); + /// /// 测试延迟 /// /// 延迟 - public int Test() + public async Task PingAsync() { try { - var destination = DnsUtils.Lookup(Hostname); + var destination = await DnsUtils.LookupAsync(Hostname); if (destination == null) return Delay = -2; var list = new Task[3]; for (var i = 0; i < 3; i++) - list[i] = Task.Run(async () => + { + async Task PingCoreAsync() { try { return Global.Settings.ServerTCPing ? await Utils.Utils.TCPingAsync(destination, Port) - : Utils.Utils.ICMPing(destination, Port); + : await Utils.Utils.ICMPingAsync(destination); } catch (Exception) { return -4; } - }); + } - Task.WaitAll(list[0], list[1], list[2]); + list[i] = PingCoreAsync(); + } - var min = Math.Min(list[0].Result, list[1].Result); - min = Math.Min(min, list[2].Result); - return Delay = min; + var resTask = await Task.WhenAny(list[0], list[1], list[2]); + + return Delay = await resTask; } catch (Exception) { @@ -111,9 +113,9 @@ namespace Netch.Models public static class ServerExtension { - public static string AutoResolveHostname(this Server server) + public static async Task AutoResolveHostnameAsync(this Server server) { - return Global.Settings.ResolveServerHostname ? DnsUtils.Lookup(server.Hostname)!.ToString() : server.Hostname; + return Global.Settings.ResolveServerHostname ? (await DnsUtils.LookupAsync(server.Hostname))!.ToString() : server.Hostname; } public static bool Valid(this Server server) diff --git a/Netch/Netch.cs b/Netch/Netch.cs index aa2dd44b..03852a5d 100644 --- a/Netch/Netch.cs +++ b/Netch/Netch.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; using System.Windows.Forms; using Windows.Win32; using Windows.Win32.Foundation; -using Windows.Win32.UI.WindowsAndMessaging; +using Microsoft.VisualStudio.Threading; using Netch.Controllers; using Netch.Forms; using Netch.Services; @@ -83,7 +83,7 @@ namespace Netch Environment.Exit(2); } - Task.Run(LogEnvironment); + Task.Run(LogEnvironment).Forget(); // 绑定错误捕获 Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); diff --git a/Netch/Netch.csproj b/Netch/Netch.csproj index c79bf4c6..a901b9eb 100644 --- a/Netch/Netch.csproj +++ b/Netch/Netch.csproj @@ -10,7 +10,7 @@ false latest enable - VSTHRD002;VSTHRD200;VSTHRD100;VSTHRD101;VSTHRD110;VSTHRD130 + VSTHRD100 false Default true diff --git a/Netch/Servers/Shadowsocks/SSController.cs b/Netch/Servers/Shadowsocks/SSController.cs index ed26cc79..64ddda5a 100644 --- a/Netch/Servers/Shadowsocks/SSController.cs +++ b/Netch/Servers/Shadowsocks/SSController.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Net; +using System.Threading.Tasks; using Netch.Controllers; using Netch.Interfaces; using Netch.Models; @@ -22,13 +23,13 @@ namespace Netch.Servers.Shadowsocks public string? LocalAddress { get; set; } - public Socks5 Start(in Server s) + public async Task StartAsync(Server s) { var server = (Shadowsocks)s; var command = new SSParameter { - s = server.AutoResolveHostname(), + s = await server.AutoResolveHostnameAsync(), p = server.Port, b = this.LocalAddress(), l = this.Socks5LocalPort(), @@ -39,7 +40,7 @@ namespace Netch.Servers.Shadowsocks plugin_opts = server.PluginOption }; - StartGuard(command.ToString()); + await StartGuardAsync(command.ToString()); return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname); } diff --git a/Netch/Servers/ShadowsocksR/SSRController.cs b/Netch/Servers/ShadowsocksR/SSRController.cs index 74db3839..5fb37640 100644 --- a/Netch/Servers/ShadowsocksR/SSRController.cs +++ b/Netch/Servers/ShadowsocksR/SSRController.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Net; +using System.Threading.Tasks; using Netch.Controllers; using Netch.Interfaces; using Netch.Models; @@ -22,13 +23,13 @@ namespace Netch.Servers.ShadowsocksR public string? LocalAddress { get; set; } - public Socks5 Start(in Server s) + public async Task StartAsync(Server s) { var server = (ShadowsocksR)s; var command = new SSRParameter { - s = server.AutoResolveHostname(), + s = await server.AutoResolveHostnameAsync(), p = server.Port, k = server.Password, m = server.EncryptMethod, @@ -42,7 +43,7 @@ namespace Netch.Servers.ShadowsocksR u = true }; - StartGuard(command.ToString()); + await StartGuardAsync(command.ToString()); return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(),server.Hostname); } diff --git a/Netch/Servers/Socks5/S5Controller.cs b/Netch/Servers/Socks5/S5Controller.cs index da82891e..d4156c2b 100644 --- a/Netch/Servers/Socks5/S5Controller.cs +++ b/Netch/Servers/Socks5/S5Controller.cs @@ -1,5 +1,5 @@ +using System.Threading.Tasks; using Netch.Models; -using Netch.Servers; namespace Netch.Servers { @@ -7,11 +7,11 @@ namespace Netch.Servers { public override string Name { get; } = "Socks5"; - public override Socks5 Start(in Server s) + public override async Task StartAsync(Server s) { var server = (Socks5)s; if (server.Auth()) - base.Start(s); + await base.StartAsync(s); return server; } diff --git a/Netch/Servers/Trojan/TrojanController.cs b/Netch/Servers/Trojan/TrojanController.cs index 7a90d994..585cdd47 100644 --- a/Netch/Servers/Trojan/TrojanController.cs +++ b/Netch/Servers/Trojan/TrojanController.cs @@ -2,6 +2,7 @@ using System.IO; using System.Net; using System.Text.Json; +using System.Threading.Tasks; using Netch.Controllers; using Netch.Interfaces; using Netch.Models; @@ -26,14 +27,14 @@ namespace Netch.Servers public string? LocalAddress { get; set; } - public Socks5 Start(in Server s) + public async Task StartAsync(Server s) { var server = (Trojan)s; var trojanConfig = new TrojanConfig { local_addr = this.LocalAddress(), local_port = this.Socks5LocalPort(), - remote_addr = server.AutoResolveHostname(), + remote_addr = await server.AutoResolveHostnameAsync(), remote_port = server.Port, password = new List { @@ -45,12 +46,12 @@ namespace Netch.Servers } }; - using (var fileStream = new FileStream(Constants.TempConfig, FileMode.Create, FileAccess.Write)) + await using (var fileStream = new FileStream(Constants.TempConfig, FileMode.Create, FileAccess.Write, FileShare.Read)) { - JsonSerializer.SerializeAsync(fileStream, trojanConfig, Global.NewCustomJsonSerializerOptions()).Wait(); + await JsonSerializer.SerializeAsync(fileStream, trojanConfig, Global.NewCustomJsonSerializerOptions()); } - StartGuard("-c ..\\data\\last.json"); + await StartGuardAsync("-c ..\\data\\last.json"); return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname); } } diff --git a/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs b/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs index c9ad6068..f328e39d 100644 --- a/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs +++ b/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs @@ -1,13 +1,16 @@ -using Netch.Models; +using System.Threading.Tasks; +using Netch.Models; using Netch.Servers.V2ray.Models; using Netch.Utils; using V2rayConfig = Netch.Servers.V2ray.Models.V2rayConfig; +#pragma warning disable VSTHRD200 + namespace Netch.Servers.Utils { public static class V2rayConfigUtils { - public static V2rayConfig GenerateClientConfig(Server server) + public static async Task GenerateClientConfigAsync(Server server) { var v2rayConfig = new V2rayConfig { @@ -26,12 +29,12 @@ namespace Netch.Servers.Utils } }; - v2rayConfig.outbounds = new[] { outbound(server) }; + v2rayConfig.outbounds = new[] { await outbound(server) }; return v2rayConfig; } - private static Outbound outbound(Server server) + private static async Task outbound(Server server) { var outbound = new Outbound { @@ -48,7 +51,7 @@ namespace Netch.Servers.Utils { new { - address = server.AutoResolveHostname(), + address = await server.AutoResolveHostnameAsync(), port = server.Port, users = socks5.Auth() ? new[] @@ -75,7 +78,7 @@ namespace Netch.Servers.Utils { new VnextItem { - address = server.AutoResolveHostname(), + address = await server.AutoResolveHostnameAsync(), port = server.Port, users = new[] { @@ -111,7 +114,7 @@ namespace Netch.Servers.Utils { new VnextItem { - address = server.AutoResolveHostname(), + address = await server.AutoResolveHostnameAsync(), port = server.Port, users = new[] { diff --git a/Netch/Servers/V2ray/V2rayController.cs b/Netch/Servers/V2ray/V2rayController.cs index e34aa5a8..ff065a84 100644 --- a/Netch/Servers/V2ray/V2rayController.cs +++ b/Netch/Servers/V2ray/V2rayController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Net; using System.Text.Json; +using System.Threading.Tasks; using Netch.Controllers; using Netch.Interfaces; using Netch.Models; @@ -27,14 +28,14 @@ namespace Netch.Servers public string? LocalAddress { get; set; } - public virtual Socks5 Start(in Server s) + public virtual async Task StartAsync(Server s) { - using (var fileStream = new FileStream(Constants.TempConfig, FileMode.Create, FileAccess.Write)) + using (var fileStream = new FileStream(Constants.TempConfig, FileMode.Create, FileAccess.Write, FileShare.Read)) { - JsonSerializer.SerializeAsync(fileStream, V2rayConfigUtils.GenerateClientConfig(s), Global.NewCustomJsonSerializerOptions()).Wait(); + await JsonSerializer.SerializeAsync(fileStream, await V2rayConfigUtils.GenerateClientConfigAsync(s), Global.NewCustomJsonSerializerOptions()); } - StartGuard("-config ..\\data\\last.json"); + await StartGuardAsync("-config ..\\data\\last.json"); return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), s.Hostname); } } diff --git a/Netch/Utils/Bandwidth.cs b/Netch/Utils/Bandwidth.cs index ffcf095e..2d708c75 100644 --- a/Netch/Utils/Bandwidth.cs +++ b/Netch/Utils/Bandwidth.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Diagnostics.Tracing.Parsers; using Microsoft.Diagnostics.Tracing.Session; +using Microsoft.VisualStudio.Threading; using Netch.Controllers; using Netch.Models; using Serilog; @@ -114,7 +115,7 @@ namespace Netch.Utils }; tSession.Source.Process(); - }); + }).Forget(); while (Global.MainForm.State != State.Stopped) { diff --git a/Netch/Utils/Configuration.cs b/Netch/Utils/Configuration.cs index 32464e83..59455edc 100644 --- a/Netch/Utils/Configuration.cs +++ b/Netch/Utils/Configuration.cs @@ -3,7 +3,6 @@ using System.IO; using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; -using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.Threading; using Netch.Models; @@ -46,11 +45,11 @@ namespace Netch.Utils return; } - if (await LoadAsyncCore(FileFullName)) + if (await LoadCoreAsync(FileFullName)) return; Log.Information("尝试加载备份配置文件 {FileName}", BackupFileFullName); - await LoadAsyncCore(BackupFileFullName); + await LoadCoreAsync(BackupFileFullName); } catch (Exception e) { @@ -59,7 +58,7 @@ namespace Netch.Utils } } - private static async ValueTask LoadAsyncCore(string filename) + private static async ValueTask LoadCoreAsync(string filename) { try { diff --git a/Netch/Utils/DnsUtils.cs b/Netch/Utils/DnsUtils.cs index 5f67400f..8357bf05 100644 --- a/Netch/Utils/DnsUtils.cs +++ b/Netch/Utils/DnsUtils.cs @@ -3,6 +3,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net; +using System.Threading.Tasks; +using Serilog; namespace Netch.Utils { @@ -13,7 +15,7 @@ namespace Netch.Utils /// private static readonly Hashtable Cache = new(); - public static IPAddress? Lookup(string hostname, int timeout = 3000) + public static async Task LookupAsync(string hostname, int timeout = 3000) { try { @@ -21,18 +23,26 @@ namespace Netch.Utils return Cache[hostname] as IPAddress; var task = Dns.GetHostAddressesAsync(hostname); - if (!task.Wait(timeout)) - return null; - if (task.Result.Length == 0) - return null; + var resTask = await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false); - Cache.Add(hostname, task.Result[0]); + if (resTask == task) + { + var result = await task; - return task.Result[0]; + if (result.Length == 0) + return null; + + Cache.Add(hostname, result[0]); + + return result[0]; + } + + return null; } - catch (Exception) + catch (Exception e) { + Log.Verbose(e, "Lookup hostname {Hostname} failed", hostname); return null; } } diff --git a/Netch/Utils/ServerHelper.cs b/Netch/Utils/ServerHelper.cs index afe23711..be93f1cf 100644 --- a/Netch/Utils/ServerHelper.cs +++ b/Netch/Utils/ServerHelper.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Microsoft.VisualStudio.Threading; using Netch.Interfaces; using Netch.Models; using Timer = System.Timers.Timer; @@ -33,6 +34,8 @@ namespace Netch.Utils 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() @@ -43,7 +46,7 @@ namespace Netch.Utils AutoReset = true }; - Timer.Elapsed += (_, _) => TestAllDelay(); + Timer.Elapsed += (_, _) => TestAllDelayAsync().Forget(); } public static bool Enabled @@ -65,17 +68,27 @@ namespace Netch.Utils return value != 0 && Range.InRange(value); } - public static event EventHandler? TestDelayFinished; - - public static void TestAllDelay() + public static async Task TestAllDelayAsync() { if (!Monitor.TryEnter(TestAllLock)) return; try { - Parallel.ForEach(Global.Settings.Server, new ParallelOptions { MaxDegreeOfParallelism = 16 }, server => { server.Test(); }); - TestDelayFinished?.Invoke(null, new EventArgs()); + var tasks = Global.Settings.Server.Select(async s => + { + await SemaphoreSlim.WaitAsync(); + try + { + await s.PingAsync(); + } + finally + { + SemaphoreSlim.Release(); + } + }); + + await Task.WhenAll(tasks); } catch (Exception) { @@ -95,8 +108,9 @@ namespace Netch.Utils return; Timer.Interval = Global.Settings.DetectionTick * 1000; - Task.Run(TestAllDelay); Timer.Start(); + + TestAllDelayAsync().Forget(); } } diff --git a/Netch/Utils/Subscription.cs b/Netch/Utils/Subscription.cs index d8b137ab..253e1e83 100644 --- a/Netch/Utils/Subscription.cs +++ b/Netch/Utils/Subscription.cs @@ -1,9 +1,9 @@ -using Netch.Models; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; +using Netch.Models; using Serilog; namespace Netch.Utils @@ -14,10 +14,10 @@ namespace Netch.Utils public static async Task UpdateServersAsync(string? proxyServer = default) { - await Task.WhenAll(Global.Settings.SubscribeLink.Select(item => Task.Run(() => UpdateServer(item, proxyServer))).ToArray()); + await Task.WhenAll(Global.Settings.SubscribeLink.Select(item => UpdateServerCoreAsync(item, proxyServer))); } - public static void UpdateServer(SubscribeLink item, string? proxyServer) + private static async Task UpdateServerCoreAsync(SubscribeLink item, string? proxyServer) { try { @@ -34,11 +34,11 @@ namespace Netch.Utils List servers; - var result = WebUtil.DownloadString(request, out var rep); - if (rep.StatusCode == HttpStatusCode.OK) + var (code, result) = await WebUtil.DownloadStringAsync(request); + if (code == HttpStatusCode.OK) servers = ShareLink.ParseText(result); else - throw new Exception($"{item.Remark} Response Status Code: {rep.StatusCode}"); + throw new Exception($"{item.Remark} Response Status Code: {code}"); foreach (var server in servers) server.Group = item.Remark; diff --git a/Netch/Utils/Utils.cs b/Netch/Utils/Utils.cs index f42ce61b..33c35ed9 100644 --- a/Netch/Utils/Utils.cs +++ b/Netch/Utils/Utils.cs @@ -57,33 +57,34 @@ namespace Netch.Utils return timeout; } - public static int ICMPing(IPAddress ip, int timeout = 1000) + public static async Task ICMPingAsync(IPAddress ip, int timeout = 1000) { - var reply = new Ping().Send(ip, timeout); + var reply = await new Ping().SendPingAsync(ip, timeout); - if (reply?.Status == IPStatus.Success) + if (reply.Status == IPStatus.Success) return Convert.ToInt32(reply.RoundtripTime); return timeout; } - public static string GetCityCode(string Hostname) + public static async Task GetCityCodeAsync(string address) { - if (Hostname.Contains(":")) - Hostname = Hostname.Split(':')[0]; + var i = address.IndexOf(':'); + if (i != -1) + address = address[..i]; string? country = null; try { var databaseReader = new DatabaseReader("bin\\GeoLite2-Country.mmdb"); - if (IPAddress.TryParse(Hostname, out _)) + if (IPAddress.TryParse(address, out _)) { - country = databaseReader.Country(Hostname).Country.IsoCode; + country = databaseReader.Country(address).Country.IsoCode; } else { - var dnsResult = DnsUtils.Lookup(Hostname); + var dnsResult = await DnsUtils.LookupAsync(address); if (dnsResult != null) country = databaseReader.Country(dnsResult).Country.IsoCode; diff --git a/Netch/Utils/WebUtil.cs b/Netch/Utils/WebUtil.cs index 458ffdea..91d93a8f 100644 --- a/Netch/Utils/WebUtil.cs +++ b/Netch/Utils/WebUtil.cs @@ -38,9 +38,9 @@ namespace Netch.Utils /// public static async Task DownloadBytesAsync(HttpWebRequest req) { - using var webResponse = req.GetResponseAsync(); + using var webResponse = await req.GetResponseAsync(); await using var memoryStream = new MemoryStream(); - await using var input = webResponse.Result.GetResponseStream(); + await using var input = webResponse.GetResponseStream(); await input.CopyToAsync(memoryStream); return memoryStream.ToArray(); @@ -53,14 +53,14 @@ namespace Netch.Utils /// /// 编码,默认UTF-8 /// - public static string DownloadString(HttpWebRequest req, out HttpWebResponse rep, Encoding? encoding = null) + public static (HttpStatusCode, string) DownloadString(HttpWebRequest req, Encoding? encoding = null) { encoding ??= Encoding.UTF8; - rep = (HttpWebResponse)req.GetResponse(); + using var rep = (HttpWebResponse)req.GetResponse(); using var responseStream = rep.GetResponseStream(); using var streamReader = new StreamReader(responseStream, encoding); - return streamReader.ReadToEnd(); + return (rep.StatusCode, streamReader.ReadToEnd()); } /// @@ -69,14 +69,14 @@ namespace Netch.Utils /// /// 编码,默认UTF-8 /// - public static async Task DownloadStringAsync(HttpWebRequest req, Encoding? encoding = null) + public static async Task<(HttpStatusCode, string)> DownloadStringAsync(HttpWebRequest req, Encoding? encoding = null) { encoding ??= Encoding.UTF8; - using var webResponse = await req.GetResponseAsync(); + using var webResponse = (HttpWebResponse)await req.GetResponseAsync(); await using var responseStream = webResponse.GetResponseStream(); using var streamReader = new StreamReader(responseStream, encoding); - return await streamReader.ReadToEndAsync(); + return (webResponse.StatusCode, await streamReader.ReadToEndAsync()); } public static async Task DownloadFileAsync(string address, string fileFullPath, IProgress? progress = null) @@ -92,7 +92,7 @@ namespace Netch.Utils using (var downloadTask = input.CopyToAsync(fileStream)) { if (progress != null) - ReportProgress(webResponse.ContentLength, downloadTask, fileStream, progress, 200).Forget(); + ReportProgressAsync(webResponse.ContentLength, downloadTask, fileStream, progress, 200).Forget(); await downloadTask; } @@ -100,7 +100,7 @@ namespace Netch.Utils progress?.Report(100); } - private static async Task ReportProgress(long total, IAsyncResult downloadTask, Stream stream, IProgress progress, int interval) + private static async Task ReportProgressAsync(long total, IAsyncResult downloadTask, Stream stream, IProgress progress, int interval) { var n = 0; while (!downloadTask.IsCompleted)