控制器日志写入线程安全,主控制器启动停止等改为异步执行,修复主控制器停止提前释放

This commit is contained in:
ChsBuffer
2020-08-08 00:31:40 +08:00
parent 689f29d0f0
commit 3d24aa7583
9 changed files with 129 additions and 151 deletions

View File

@@ -105,7 +105,7 @@ namespace Netch.Controllers
/// <param name="argument">主程序启动参数</param> /// <param name="argument">主程序启动参数</param>
/// <param name="priority">进程优先级</param> /// <param name="priority">进程优先级</param>
/// <returns>是否成功启动</returns> /// <returns>是否成功启动</returns>
protected bool StartInstanceAuto(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal) protected bool StartInstanceAuto(string argument, ProcessPriorityClass priority = ProcessPriorityClass.RealTime)
{ {
State = State.Starting; State = State.Starting;
try try
@@ -183,14 +183,14 @@ namespace Netch.Controllers
/// </summary> /// </summary>
/// <param name="sender">发送者</param> /// <param name="sender">发送者</param>
/// <param name="e">数据</param> /// <param name="e">数据</param>
protected async void OnOutputDataReceived(object sender, DataReceivedEventArgs e) protected void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{ {
// 程序结束, 接收到 null // 程序结束, 接收到 null
if (e.Data == null) if (e.Data == null)
return; return;
var info = Encoding.GetEncoding(InstanceOutputEncoding).GetBytes(e.Data + Global.EOF); var info = Encoding.GetEncoding(InstanceOutputEncoding).GetBytes(e.Data + Global.EOF);
await Write(info); Task.Run(() => Write(info));
var str = Encoding.UTF8.GetString(info); var str = Encoding.UTF8.GetString(info);
// 检查启动 // 检查启动
if (State == State.Starting) if (State == State.Starting)
@@ -220,19 +220,24 @@ namespace Netch.Controllers
} }
} }
private readonly object _fileLocker = new object();
/// <summary> /// <summary>
/// 写入日志文件流 /// 写入日志文件流
/// </summary> /// </summary>
/// <param name="info"></param> /// <param name="info"></param>
/// <returns>转码后的字符串</returns> /// <returns>转码后的字符串</returns>
private async Task Write(byte[] info) private void Write(byte[] info)
{ {
if (info == null) if (info == null)
return; return;
try try
{ {
await _logFileStream.WriteAsync(info, 0, info.Length); lock (_fileLocker)
{
_logFileStream.Write(info, 0, info.Length);
}
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -72,7 +72,7 @@ namespace Netch.Controllers
/// <param name="server">服务器</param> /// <param name="server">服务器</param>
/// <param name="mode">模式</param> /// <param name="mode">模式</param>
/// <returns>是否启动成功</returns> /// <returns>是否启动成功</returns>
public bool Start(Server server, Mode mode) public async Task<bool> Start(Server server, Mode mode)
{ {
Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}"); Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}");
@@ -88,6 +88,7 @@ namespace Netch.Controllers
#endregion #endregion
FlushDNSResolverCache(); FlushDNSResolverCache();
_ = Task.Run(Firewall.AddNetchFwRules);
bool result; bool result;
if (server.Type == "Socks5") if (server.Type == "Socks5")
@@ -111,17 +112,17 @@ namespace Netch.Controllers
var isPortNotAvailable = false; var isPortNotAvailable = false;
if (_savedServer.Type != "Socks5") if (_savedServer.Type != "Socks5")
{ {
isPortNotAvailable = PortCheckAndShowMessageBox(_socks5Port, "Socks5"); isPortNotAvailable |= PortCheckAndShowMessageBox(_socks5Port, "Socks5");
} }
switch (_savedMode.Type) switch (_savedMode.Type)
{ {
case 0: case 0:
isPortNotAvailable = isPortNotAvailable || PortCheckAndShowMessageBox(_redirectorTCPPort, "Redirector TCP"); isPortNotAvailable |= PortCheckAndShowMessageBox(_redirectorTCPPort, "Redirector TCP");
break; break;
case 3: case 3:
case 5: case 5:
isPortNotAvailable = isPortNotAvailable || PortCheckAndShowMessageBox(_httpPort, "HTTP"); isPortNotAvailable |= PortCheckAndShowMessageBox(_httpPort, "HTTP");
break; break;
} }
@@ -132,7 +133,7 @@ namespace Netch.Controllers
} }
Global.MainForm.StatusText(i18N.Translate("Starting ", pEncryptedProxyController.Name)); Global.MainForm.StatusText(i18N.Translate("Starting ", pEncryptedProxyController.Name));
result = pEncryptedProxyController.Start(server, mode); result = await Task.Run(() => pEncryptedProxyController.Start(server, mode));
} }
if (result) if (result)
@@ -161,7 +162,7 @@ namespace Netch.Controllers
if (pModeController != null) if (pModeController != null)
{ {
Global.MainForm.StatusText(i18N.Translate("Starting ", pModeController.Name)); Global.MainForm.StatusText(i18N.Translate("Starting ", pModeController.Name));
result = pModeController.Start(server, mode); result = await Task.Run(() => pModeController.Start(server, mode));
} }
if (result) if (result)
@@ -179,13 +180,12 @@ namespace Netch.Controllers
break; break;
} }
switch (mode.Type) switch (mode.Type)
{ {
case 0: case 0:
case 1: case 1:
case 2: case 2:
Task.Run(() => _ = Task.Run(() =>
{ {
Global.MainForm.NatTypeStatusText(i18N.Translate("Starting NatTester")); Global.MainForm.NatTypeStatusText(i18N.Translate("Starting NatTester"));
// Thread.Sleep(1000); // Thread.Sleep(1000);
@@ -202,7 +202,7 @@ namespace Netch.Controllers
if (!result) if (!result)
{ {
Logging.Error("主控制器启动失败"); Logging.Error("主控制器启动失败");
Stop(); await Stop();
} }
return result; return result;
@@ -211,15 +211,16 @@ namespace Netch.Controllers
/// <summary> /// <summary>
/// 停止 /// 停止
/// </summary> /// </summary>
public async void Stop() public async Task Stop()
{ {
await Task.WhenAll(new[] var tasks = new Task[]
{ {
Task.Run(() => pEncryptedProxyController?.Stop()), Task.Run(() => pEncryptedProxyController?.Stop()),
Task.Run(() => UsingPorts.Clear()), Task.Run(() => UsingPorts.Clear()),
Task.Run(() => pModeController?.Stop()), Task.Run(() => pModeController?.Stop()),
Task.Run(() => pNTTController.Stop()) Task.Run(() => pNTTController.Stop())
}); };
await Task.WhenAll(tasks);
} }
public static void KillProcessByName(string name) public static void KillProcessByName(string name)

View File

@@ -175,18 +175,9 @@ namespace Netch.Controllers
} }
if (!File.Exists(SystemDriver)) return true; if (!File.Exists(SystemDriver)) return true;
NFAPI.nf_unRegisterDriver("netfilter2");
try
{
NFAPI.nf_unRegisterDriver("netfilter2");
}
catch (Exception e)
{
Logging.Error(e.ToString());
return false;
}
File.Delete(SystemDriver); File.Delete(SystemDriver);
return true; return true;
} }

View File

@@ -40,26 +40,22 @@ namespace Netch.Forms
State = State.Starting; State = State.Starting;
await Task.Run(Firewall.AddNetchFwRules);
var server = ServerComboBox.SelectedItem as Models.Server; var server = ServerComboBox.SelectedItem as Models.Server;
var mode = ModeComboBox.SelectedItem as Models.Mode; var mode = ModeComboBox.SelectedItem as Models.Mode;
var result = false; var result = false;
await Task.Run(() => try
{ {
try // TODO 完善控制器异常处理
{ result = await _mainController.Start(server, mode);
// TODO 完善控制器异常处理 }
result = _mainController.Start(server, mode); catch (Exception e)
} {
catch (Exception e) if (e is DllNotFoundException || e is FileNotFoundException)
{ MessageBoxX.Show(e.Message + "\n\n" + i18N.Translate("Missing File or runtime components"), owner: this);
if (e is DllNotFoundException || e is FileNotFoundException) Netch.Application_OnException(null, new ThreadExceptionEventArgs(e));
MessageBoxX.Show(e.Message + "\n\n" + i18N.Translate("Missing File or runtime components"), owner: this); }
Netch.Application_OnException(null, new ThreadExceptionEventArgs(e));
}
});
if (result) if (result)
{ {
@@ -106,7 +102,7 @@ namespace Netch.Forms
{ {
// 停止 // 停止
State = State.Stopping; State = State.Stopping;
_mainController.Stop(); await _mainController.Stop();
State = State.Stopped; State = State.Stopped;
_ = Task.Run(TestServer); _ = Task.Run(TestServer);
} }

View File

@@ -128,7 +128,7 @@ namespace Netch.Forms
Remark = "ProxyUpdate", Remark = "ProxyUpdate",
Type = 5 Type = 5
}; };
_mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode); await _mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
} }
var serverLock = new object(); var serverLock = new object();
@@ -157,16 +157,14 @@ namespace Netch.Forms
lock (serverLock) lock (serverLock)
{ {
Global.Settings.Server = Global.Settings.Server.Where(server => server.Group != item.Remark).ToList(); Global.Settings.Server = Global.Settings.Server.Where(server => server.Group != item.Remark).ToList();
} var result = ShareLink.Parse(str);
if (result != null)
{
foreach (var x in result) x.Group = item.Remark;
var result = ShareLink.Parse(str); Global.Settings.Server.AddRange(result);
NotifyTip(i18N.TranslateFormat("Update {1} server(s) from {0}", item.Remark, result.Count));
if (result != null) }
{
foreach (var x in result) x.Group = item.Remark;
Global.Settings.Server.AddRange(result);
NotifyTip(i18N.TranslateFormat("Update {1} server(s) from {0}", item.Remark, result.Count));
} }
} }
catch (WebException e) catch (WebException e)
@@ -186,7 +184,7 @@ namespace Netch.Forms
if (Global.Settings.UseProxyToUpdateSubscription) if (Global.Settings.UseProxyToUpdateSubscription)
{ {
_mainController.Stop(); await _mainController.Stop();
} }
} }
@@ -199,13 +197,10 @@ namespace Netch.Forms
Utils.Utils.Open(".\\"); Utils.Utils.Open(".\\");
} }
private void CleanDNSCacheToolStripMenuItem_Click(object sender, EventArgs e) private async void CleanDNSCacheToolStripMenuItem_Click(object sender, EventArgs e)
{ {
Task.Run(() => await Task.Run(() => DNS.Cache.Clear());
{ StatusText(i18N.Translate("DNS cache cleanup succeeded"));
DNS.Cache.Clear();
NotifyTip(i18N.Translate("DNS cache cleanup succeeded"));
});
} }
private void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e) private void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
@@ -246,95 +241,83 @@ namespace Netch.Forms
DisableItems(false); DisableItems(false);
await Task.Run(async () => if (useProxy)
{
var mode = new Models.Mode
{
Remark = "ProxyUpdate",
Type = 5
};
State = State.Starting;
await _mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
}
NotifyTip(i18N.Translate("Updating in the background"));
try
{
var req = WebUtil.CreateRequest(Global.Settings.ACL);
if (useProxy)
req.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
await WebUtil.DownloadFileAsync(req, Path.Combine(Global.NetchDir, "bin\\default.acl"));
NotifyTip(i18N.Translate("ACL updated successfully"));
}
catch (Exception e)
{
NotifyTip(i18N.Translate("ACL update failed") + "\n" + e.Message, info: false);
Logging.Error("更新 ACL 失败!" + e);
}
finally
{ {
if (useProxy) if (useProxy)
{ {
var mode = new Models.Mode await _mainController.Stop();
{ State = State.Stopped;
Remark = "ProxyUpdate",
Type = 5
};
State = State.Starting;
_mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
} }
NotifyTip(i18N.Translate("Updating in the background")); DisableItems(true);
try }
{
var req = WebUtil.CreateRequest(Global.Settings.ACL);
if (useProxy)
req.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
await WebUtil.DownloadFileAsync(req, Path.Combine(Global.NetchDir, "bin\\default.acl"));
NotifyTip(i18N.Translate("ACL updated successfully"));
}
catch (Exception e)
{
NotifyTip(i18N.Translate("ACL update failed") + "\n" + e.Message, info: false);
Logging.Error("更新 ACL 失败!" + e);
}
finally
{
if (useProxy)
{
_mainController.Stop();
State = State.Stopped;
}
DisableItems(true);
}
});
} }
private async void UninstallServiceToolStripMenuItem_Click(object sender, EventArgs e)
private void UninstallServiceToolStripMenuItem_Click(object sender, EventArgs e)
{ {
Enabled = false; Enabled = false;
StatusText(i18N.Translate("Uninstalling NF Service")); StatusText(i18N.Translate("Uninstalling NF Service"));
Task.Run(() => var result = false;
try
{ {
try await Task.Run(() => result = NFController.UninstallDriver());
if (result)
{ {
if (NFController.UninstallDriver()) StatusText(i18N.Translate("Service has been uninstalled"));
{
StatusText(i18N.Translate("Service has been uninstalled"));
}
} }
catch (Exception e) }
{ finally
MessageBoxX.Show(e.ToString(), LogLevel.ERROR); {
Console.WriteLine(e);
throw;
}
Enabled = true; Enabled = true;
}); }
} }
private void reinstallTapDriverToolStripMenuItem_Click(object sender, EventArgs e) private async void reinstallTapDriverToolStripMenuItem_Click(object sender, EventArgs e)
{ {
Task.Run(() => StatusText(i18N.Translate("Reinstalling TUN/TAP driver"));
Enabled = false;
await Task.Run(() =>
{ {
StatusText(i18N.Translate("Reinstalling TUN/TAP driver"));
Enabled = false;
try try
{ {
Configuration.deltapall(); Configuration.deltapall();
Configuration.addtap(); Configuration.addtap();
NotifyTip(i18N.Translate("Reinstall TUN/TAP driver successfully")); StatusText(i18N.Translate("Reinstall TUN/TAP driver successfully"));
} }
catch catch
{ {
NotifyTip(i18N.Translate("Reinstall TUN/TAP driver failed"), info: false); NotifyTip(i18N.Translate("Reinstall TUN/TAP driver failed"), info: false);
} }
finally
{
State = State.Waiting;
Enabled = true;
}
}); });
State = State.Waiting;
Enabled = true;
} }
#endregion #endregion

View File

@@ -9,7 +9,10 @@ using Netch.Utils;
namespace Netch.Forms namespace Netch.Forms
{ {
public partial class Dummy { } public partial class Dummy
{
}
partial class MainForm partial class MainForm
{ {
/// init at <see cref="MainForm_Load"/> /// init at <see cref="MainForm_Load"/>
@@ -136,7 +139,7 @@ namespace Netch.Forms
private List<Button> ProfileButtons = new List<Button>(); private List<Button> ProfileButtons = new List<Button>();
private void ProfileButton_Click(object sender, EventArgs e) private async void ProfileButton_Click(object sender, EventArgs e)
{ {
var index = ProfileButtons.IndexOf((Button) sender); var index = ProfileButtons.IndexOf((Button) sender);
@@ -159,6 +162,7 @@ namespace Netch.Forms
SaveProfile(index); SaveProfile(index);
ProfileButtons[index].Text = ProfileNameText.Text; ProfileButtons[index].Text = ProfileNameText.Text;
} }
return; return;
} }
@@ -185,26 +189,20 @@ namespace Netch.Forms
ControlFun(); ControlFun();
if (State == State.Stopping || State == State.Stopped) if (State == State.Stopping || State == State.Stopped)
{ {
Task.Run(() => while (State != State.Stopped)
{ {
while (State != State.Stopped) await Task.Delay(250);
{ }
Thread.Sleep(250);
}
ControlFun(); ControlFun();
});
} }
} }
catch (Exception ee) catch (Exception ee)
{ {
Task.Run(() => Logging.Info(ee.ToString());
{ ProfileButtons[index].Text = i18N.Translate("Error");
Logging.Info(ee.ToString()); await Task.Delay(1200);
ProfileButtons[index].Text = i18N.Translate("Error"); ProfileButtons[index].Text = i18N.Translate("None");
Thread.Sleep(1200);
ProfileButtons[index].Text = i18N.Translate("None");
});
} }
} }
} }

View File

@@ -24,6 +24,7 @@ namespace Netch.Forms
{ {
if (InvokeRequired) if (InvokeRequired)
{ {
// TODO:使所有 State 赋值不在线程中执行然后移除此代码块
BeginInvoke(new Action(() => { State = value; })); BeginInvoke(new Action(() => { State = value; }));
return; return;
} }
@@ -33,9 +34,9 @@ namespace Netch.Forms
ServerComboBox.Enabled = ServerComboBox.Enabled =
ModeComboBox.Enabled = ModeComboBox.Enabled =
EditModePictureBox.Enabled = EditModePictureBox.Enabled =
EditServerPictureBox.Enabled = EditServerPictureBox.Enabled =
DeleteModePictureBox.Enabled = DeleteModePictureBox.Enabled =
DeleteServerPictureBox.Enabled = enabled; DeleteServerPictureBox.Enabled = enabled;
// 启动需要禁用的控件 // 启动需要禁用的控件
UninstallServiceToolStripMenuItem.Enabled = UninstallServiceToolStripMenuItem.Enabled =

View File

@@ -250,18 +250,15 @@ namespace Netch.Forms
ControlFun(); ControlFun();
} }
Task.Run(() => for (var i = 0; i < 16; i++)
{ {
for (var i = 0; i < 16; i++) if (State == State.Waiting || State == State.Stopped)
{ break;
if (State == State.Waiting || State == State.Stopped) Thread.Sleep(250);
break; }
Thread.Sleep(250);
}
SaveConfigs(); SaveConfigs();
State = State.Terminating; State = State.Terminating;
});
} }
#region MISC #region MISC
@@ -467,6 +464,7 @@ namespace Netch.Forms
private void NotifyTip(string text, int timeout = 0, bool info = true) private void NotifyTip(string text, int timeout = 0, bool info = true)
{ {
// 会阻塞线程 timeout 秒
NotifyIcon.ShowBalloonTip(timeout, NotifyIcon.ShowBalloonTip(timeout,
UpdateChecker.Name, UpdateChecker.Name,
text, text,

View File

@@ -35,9 +35,14 @@ namespace Netch.Utils
Write(text, LogLevel.ERROR); Write(text, LogLevel.ERROR);
} }
private static readonly object FileLock = new object();
private static void Write(string text, LogLevel logLevel) private static void Write(string text, LogLevel logLevel)
{ {
File.AppendAllText(LogFile, $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Global.EOF}"); lock (FileLock)
{
File.AppendAllText(LogFile, $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Global.EOF}");
}
} }
} }
} }