Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b26271a4f9 | ||
|
|
24f8aefefa | ||
|
|
07af80d3ae | ||
|
|
8dd0470bcf | ||
|
|
dc878569ad |
+1
-1
@@ -1 +1 @@
|
|||||||
1.0.23
|
1.0.26
|
||||||
|
|||||||
@@ -19,8 +19,22 @@ namespace MonsterMonitor.Models
|
|||||||
public bool SavePassword { get; set; } = false;
|
public bool SavePassword { get; set; } = false;
|
||||||
public int RemotePort { get; set; } = 3328;
|
public int RemotePort { get; set; } = 3328;
|
||||||
public int LocalPort { get; set; } = 7829;
|
public int LocalPort { get; set; } = 7829;
|
||||||
|
// Устарело: раньше использовалось как порог молчания heartbeat в секундах,
|
||||||
|
// но код брал Math.Max(5, MaxPingFailures), поэтому значение <5 ничего не меняло.
|
||||||
|
// Оставлено для совместимости со старыми settings.ini, см. HeartbeatSilenceSec.
|
||||||
public int MaxPingFailures { get; set; } = 3;
|
public int MaxPingFailures { get; set; } = 3;
|
||||||
public int ReconnectTimeoutSec { get; set; } = 45;
|
public int ReconnectTimeoutSec { get; set; } = 45;
|
||||||
|
|
||||||
|
// Сколько секунд молчания heartbeat считать подозрительным. Само по себе
|
||||||
|
// молчание больше НЕ рвёт соединение - оно лишь повод сделать e2e-проверку.
|
||||||
|
// Большая выгрузка легко забивает канал на десятки секунд.
|
||||||
|
public int HeartbeatSilenceSec { get; set; } = 60;
|
||||||
|
|
||||||
|
// e2e-проверка проброса: раз в сколько секунд, таймаут и сколько подряд
|
||||||
|
// неудач требуется, чтобы переподключаться.
|
||||||
|
public int ProbeIntervalSec { get; set; } = 30;
|
||||||
|
public int ProbeTimeoutSec { get; set; } = 10;
|
||||||
|
public int ProbeFailuresBeforeReconnect { get; set; } = 2;
|
||||||
public string Proxy { get; set; } = string.Empty;
|
public string Proxy { get; set; } = string.Empty;
|
||||||
public string SsProcessPath { get; set; } = Path.Combine("App_Data", "ss", "ss.exe");
|
public string SsProcessPath { get; set; } = Path.Combine("App_Data", "ss", "ss.exe");
|
||||||
public string SsArguments { get; set; } = string.Empty;
|
public string SsArguments { get; set; } = string.Empty;
|
||||||
@@ -164,6 +178,10 @@ namespace MonsterMonitor.Models
|
|||||||
result.LocalPort = GetInt(map, nameof(LocalPort), result.LocalPort);
|
result.LocalPort = GetInt(map, nameof(LocalPort), result.LocalPort);
|
||||||
result.MaxPingFailures = GetInt(map, nameof(MaxPingFailures), result.MaxPingFailures);
|
result.MaxPingFailures = GetInt(map, nameof(MaxPingFailures), result.MaxPingFailures);
|
||||||
result.ReconnectTimeoutSec = GetInt(map, nameof(ReconnectTimeoutSec), result.ReconnectTimeoutSec);
|
result.ReconnectTimeoutSec = GetInt(map, nameof(ReconnectTimeoutSec), result.ReconnectTimeoutSec);
|
||||||
|
result.HeartbeatSilenceSec = GetInt(map, nameof(HeartbeatSilenceSec), result.HeartbeatSilenceSec);
|
||||||
|
result.ProbeIntervalSec = GetInt(map, nameof(ProbeIntervalSec), result.ProbeIntervalSec);
|
||||||
|
result.ProbeTimeoutSec = GetInt(map, nameof(ProbeTimeoutSec), result.ProbeTimeoutSec);
|
||||||
|
result.ProbeFailuresBeforeReconnect = GetInt(map, nameof(ProbeFailuresBeforeReconnect), result.ProbeFailuresBeforeReconnect);
|
||||||
result.Proxy = Get(map, nameof(Proxy), result.Proxy);
|
result.Proxy = Get(map, nameof(Proxy), result.Proxy);
|
||||||
result.SsProcessPath = Get(map, nameof(SsProcessPath), result.SsProcessPath);
|
result.SsProcessPath = Get(map, nameof(SsProcessPath), result.SsProcessPath);
|
||||||
result.SsArguments = Get(map, nameof(SsArguments), result.SsArguments);
|
result.SsArguments = Get(map, nameof(SsArguments), result.SsArguments);
|
||||||
@@ -193,6 +211,10 @@ namespace MonsterMonitor.Models
|
|||||||
$"{nameof(LocalPort)}={LocalPort}",
|
$"{nameof(LocalPort)}={LocalPort}",
|
||||||
$"{nameof(MaxPingFailures)}={MaxPingFailures}",
|
$"{nameof(MaxPingFailures)}={MaxPingFailures}",
|
||||||
$"{nameof(ReconnectTimeoutSec)}={ReconnectTimeoutSec}",
|
$"{nameof(ReconnectTimeoutSec)}={ReconnectTimeoutSec}",
|
||||||
|
$"{nameof(HeartbeatSilenceSec)}={HeartbeatSilenceSec}",
|
||||||
|
$"{nameof(ProbeIntervalSec)}={ProbeIntervalSec}",
|
||||||
|
$"{nameof(ProbeTimeoutSec)}={ProbeTimeoutSec}",
|
||||||
|
$"{nameof(ProbeFailuresBeforeReconnect)}={ProbeFailuresBeforeReconnect}",
|
||||||
$"{nameof(Proxy)}={Proxy}",
|
$"{nameof(Proxy)}={Proxy}",
|
||||||
$"{nameof(SsProcessPath)}={SsProcessPath}",
|
$"{nameof(SsProcessPath)}={SsProcessPath}",
|
||||||
$"{nameof(SsArguments)}={SsArguments}"
|
$"{nameof(SsArguments)}={SsArguments}"
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
<LangVersion>7.3</LangVersion>
|
<LangVersion>7.3</LangVersion>
|
||||||
<RootNamespace>MonsterMonitor</RootNamespace>
|
<RootNamespace>MonsterMonitor</RootNamespace>
|
||||||
<AssemblyName>devenv</AssemblyName>
|
<AssemblyName>devenv</AssemblyName>
|
||||||
<AssemblyVersion>1.0.23</AssemblyVersion>
|
<AssemblyVersion>1.0.26</AssemblyVersion>
|
||||||
<Version>1.0.23</Version>
|
<Version>1.0.26</Version>
|
||||||
<FileVersion>1.0.23</FileVersion>
|
<FileVersion>1.0.26</FileVersion>
|
||||||
<InformationalVersion>1.0.23</InformationalVersion>
|
<InformationalVersion>1.0.26</InformationalVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Costura.Fody" Version="6.0.0">
|
<PackageReference Include="Costura.Fody" Version="6.0.0">
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MonsterMonitor.Models;
|
using MonsterMonitor.Models;
|
||||||
@@ -14,6 +16,13 @@ namespace MonsterMonitor.Services
|
|||||||
private readonly object _sync = new object();
|
private readonly object _sync = new object();
|
||||||
private SshClient _client;
|
private SshClient _client;
|
||||||
private ForwardedPortRemote _forwardedPort;
|
private ForwardedPortRemote _forwardedPort;
|
||||||
|
// Локальный проброс на серверный RemotePort: даёт замкнутый e2e-маршрут
|
||||||
|
// local -> SSH -> сервер:RemotePort -> обратный проброс -> наш LocalPort.
|
||||||
|
private ForwardedPortLocal _probePort;
|
||||||
|
private int _probeLocalPort;
|
||||||
|
private int _probeFailures;
|
||||||
|
private DateTime _lastProbeUtc = DateTime.MinValue;
|
||||||
|
private DateTime _lastGoodProbeUtc = DateTime.MinValue;
|
||||||
private SshCommand _heartbeatCommand;
|
private SshCommand _heartbeatCommand;
|
||||||
private CancellationTokenSource _heartbeatReadTokenSource;
|
private CancellationTokenSource _heartbeatReadTokenSource;
|
||||||
private Task _heartbeatReadTask;
|
private Task _heartbeatReadTask;
|
||||||
@@ -63,7 +72,11 @@ namespace MonsterMonitor.Services
|
|||||||
_forwardedPort.Exception += ForwardedPortOnException;
|
_forwardedPort.Exception += ForwardedPortOnException;
|
||||||
_client.AddForwardedPort(_forwardedPort);
|
_client.AddForwardedPort(_forwardedPort);
|
||||||
_forwardedPort.Start();
|
_forwardedPort.Start();
|
||||||
|
StartProbePortNoLock();
|
||||||
StartRemoteHeartbeatNoLock();
|
StartRemoteHeartbeatNoLock();
|
||||||
|
_probeFailures = 0;
|
||||||
|
_lastProbeUtc = DateTime.UtcNow;
|
||||||
|
_lastGoodProbeUtc = DateTime.UtcNow;
|
||||||
|
|
||||||
_log.Info(
|
_log.Info(
|
||||||
$"SSH подключен. Туннель remote:{_settings.RemotePort} -> local:{_settings.LocalPort}");
|
$"SSH подключен. Туннель remote:{_settings.RemotePort} -> local:{_settings.LocalPort}");
|
||||||
@@ -94,20 +107,63 @@ namespace MonsterMonitor.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var silenceThresholdSec = Math.Max(
|
if (!IsConnected())
|
||||||
5,
|
{
|
||||||
_settings.MaxPingFailures);
|
_log.Warn("SSH-сессия не подключена. Переподключаю.");
|
||||||
|
await Reconnect().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
var silenceThresholdSec = Math.Max(5, _settings.HeartbeatSilenceSec);
|
||||||
|
var probeIntervalSec = Math.Max(5, _settings.ProbeIntervalSec);
|
||||||
|
|
||||||
var lastHeartbeat = _lastHeartbeatUtc;
|
var lastHeartbeat = _lastHeartbeatUtc;
|
||||||
var isHeartbeatAlive = lastHeartbeat != DateTime.MinValue &&
|
var heartbeatSilentSec = lastHeartbeat == DateTime.MinValue
|
||||||
(DateTime.UtcNow - lastHeartbeat).TotalSeconds <= silenceThresholdSec;
|
? double.MaxValue
|
||||||
|
: (now - lastHeartbeat).TotalSeconds;
|
||||||
|
var heartbeatSuspicious = heartbeatSilentSec > silenceThresholdSec;
|
||||||
|
|
||||||
if (!IsConnected() || !isHeartbeatAlive)
|
// Heartbeat живёт в том же SSH-соединении, что и полезный трафик,
|
||||||
|
// поэтому под большой выгрузкой он опаздывает - это НЕ повод рвать
|
||||||
|
// туннель. Решение принимает только e2e-проверка проброса.
|
||||||
|
var needProbe = heartbeatSuspicious ||
|
||||||
|
(now - _lastProbeUtc).TotalSeconds >= probeIntervalSec;
|
||||||
|
|
||||||
|
if (needProbe)
|
||||||
{
|
{
|
||||||
_log.Warn("Нет живого вывода heartbeat-команды на удаленном сервере. Переподключаю SSH.");
|
_lastProbeUtc = now;
|
||||||
|
var ok = await ProbeForwardAsync().ConfigureAwait(false);
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
if (_probeFailures > 0)
|
||||||
|
{
|
||||||
|
_log.Info("e2e-проверка проброса снова проходит.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_probeFailures = 0;
|
||||||
|
_lastGoodProbeUtc = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_probeFailures++;
|
||||||
|
_log.Warn($"e2e-проверка проброса не прошла ({_probeFailures} подряд).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heartbeatSuspicious && _probeFailures == 0)
|
||||||
|
{
|
||||||
|
_log.Debug(
|
||||||
|
$"Heartbeat молчит {heartbeatSilentSec:F0}с, но проброс работает - вероятно идёт передача. Не переподключаю.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_probeFailures >= Math.Max(1, _settings.ProbeFailuresBeforeReconnect))
|
||||||
|
{
|
||||||
|
_log.Warn("Проброс портов не пропускает трафик. Переподключаю SSH.");
|
||||||
await Reconnect().ConfigureAwait(false);
|
await Reconnect().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_log.Warn("Исключение мониторинга heartbeat: " + ex.Message);
|
_log.Warn("Исключение мониторинга heartbeat: " + ex.Message);
|
||||||
@@ -183,6 +239,7 @@ namespace MonsterMonitor.Services
|
|||||||
private void DisconnectCore()
|
private void DisconnectCore()
|
||||||
{
|
{
|
||||||
StopRemoteHeartbeatNoLock();
|
StopRemoteHeartbeatNoLock();
|
||||||
|
StopProbePortNoLock();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -219,6 +276,113 @@ namespace MonsterMonitor.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Поднимает локальный проброс на серверный RemotePort. Подключение к нему
|
||||||
|
/// проходит весь маршрут целиком: наш процесс -> SSH -> слушатель обратного
|
||||||
|
/// проброса на сервере -> SSH обратно -> наш LocalPort (ss.exe).
|
||||||
|
/// Успешный TCP-connect означает, что проброс реально пропускает трафик,
|
||||||
|
/// а не просто "сессия жива".
|
||||||
|
/// </summary>
|
||||||
|
private void StartProbePortNoLock()
|
||||||
|
{
|
||||||
|
StopProbePortNoLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_probeLocalPort = FindFreeLocalPort();
|
||||||
|
_probePort = new ForwardedPortLocal(
|
||||||
|
"127.0.0.1",
|
||||||
|
(uint)_probeLocalPort,
|
||||||
|
"127.0.0.1",
|
||||||
|
(uint)_settings.RemotePort);
|
||||||
|
_client.AddForwardedPort(_probePort);
|
||||||
|
_probePort.Start();
|
||||||
|
_log.Info($"e2e-проверка проброса включена (127.0.0.1:{_probeLocalPort}).");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_probePort = null;
|
||||||
|
_log.Warn("Не удалось поднять порт e2e-проверки: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopProbePortNoLock()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_probePort != null)
|
||||||
|
{
|
||||||
|
if (_probePort.IsStarted)
|
||||||
|
{
|
||||||
|
_probePort.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
_probePort.Dispose();
|
||||||
|
_probePort = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore errors on shutdown.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int FindFreeLocalPort()
|
||||||
|
{
|
||||||
|
var listener = new TcpListener(IPAddress.Loopback, 0);
|
||||||
|
listener.Start();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
listener.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> ProbeForwardAsync()
|
||||||
|
{
|
||||||
|
ForwardedPortLocal probe;
|
||||||
|
int port;
|
||||||
|
lock (_sync)
|
||||||
|
{
|
||||||
|
probe = _probePort;
|
||||||
|
port = _probeLocalPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probe == null || !probe.IsStarted || port <= 0)
|
||||||
|
{
|
||||||
|
// Порт проверки поднять не удалось - не выдумываем отказ проброса,
|
||||||
|
// иначе будем рвать рабочий туннель из-за собственной диагностики.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeoutMs = Math.Max(1, _settings.ProbeTimeoutSec) * 1000;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var tcp = new TcpClient())
|
||||||
|
{
|
||||||
|
var connectTask = tcp.ConnectAsync(IPAddress.Loopback, port);
|
||||||
|
var completed = await Task.WhenAny(connectTask, Task.Delay(timeoutMs)).ConfigureAwait(false);
|
||||||
|
if (completed != connectTask)
|
||||||
|
{
|
||||||
|
_log.Debug("e2e-проверка: таймаут подключения через проброс.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await connectTask.ConfigureAwait(false);
|
||||||
|
return tcp.Connected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Debug("e2e-проверка: " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void StartRemoteHeartbeatNoLock()
|
private void StartRemoteHeartbeatNoLock()
|
||||||
{
|
{
|
||||||
if (_client == null || !_client.IsConnected)
|
if (_client == null || !_client.IsConnected)
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using MonsterMonitor.Models;
|
|
||||||
using MonsterMonitor.Services;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using static System.Net.Mime.MediaTypeNames;
|
using MonsterMonitor.Models;
|
||||||
|
using MonsterMonitor.Services;
|
||||||
|
|
||||||
namespace MonsterMonitor.UI
|
namespace MonsterMonitor.UI
|
||||||
{
|
{
|
||||||
@@ -30,7 +29,7 @@ namespace MonsterMonitor.UI
|
|||||||
|
|
||||||
public MainForm()
|
public MainForm()
|
||||||
{
|
{
|
||||||
Text = string.Format("Monster Monitor v{0}", System.Windows.Forms.Application.ProductVersion);
|
Text = string.Format("Monster Monitor v{0}", Application.ProductVersion);
|
||||||
Width = 980;
|
Width = 980;
|
||||||
Height = 620;
|
Height = 620;
|
||||||
StartPosition = FormStartPosition.CenterScreen;
|
StartPosition = FormStartPosition.CenterScreen;
|
||||||
@@ -191,18 +190,11 @@ namespace MonsterMonitor.UI
|
|||||||
|
|
||||||
private void AppendLog(LogEntry entry)
|
private void AppendLog(LogEntry entry)
|
||||||
{
|
{
|
||||||
if (_console.Text.Length > 100000)
|
|
||||||
{
|
|
||||||
_console.ResetText();
|
|
||||||
}
|
|
||||||
System.Windows.Forms.Application.DoEvents();
|
|
||||||
|
|
||||||
if (InvokeRequired)
|
if (InvokeRequired)
|
||||||
{
|
{
|
||||||
BeginInvoke(new Action<LogEntry>(AppendLog), entry);
|
BeginInvoke(new Action<LogEntry>(AppendLog), entry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
System.Windows.Forms.Application.DoEvents();
|
|
||||||
|
|
||||||
var wasNearBottom = IsConsoleNearBottom();
|
var wasNearBottom = IsConsoleNearBottom();
|
||||||
var firstVisibleLineBeforeAppend = GetFirstVisibleLine(_console);
|
var firstVisibleLineBeforeAppend = GetFirstVisibleLine(_console);
|
||||||
@@ -212,14 +204,12 @@ namespace MonsterMonitor.UI
|
|||||||
_console.SelectionColor = GetColor(entry.Level);
|
_console.SelectionColor = GetColor(entry.Level);
|
||||||
_console.AppendText($"[{entry.Timestamp:HH:mm:ss}] [{entry.Level}] {entry.Message}{Environment.NewLine}");
|
_console.AppendText($"[{entry.Timestamp:HH:mm:ss}] [{entry.Level}] {entry.Message}{Environment.NewLine}");
|
||||||
_console.SelectionColor = _console.ForeColor;
|
_console.SelectionColor = _console.ForeColor;
|
||||||
System.Windows.Forms.Application.DoEvents();
|
|
||||||
|
|
||||||
if (wasNearBottom)
|
if (wasNearBottom)
|
||||||
{
|
{
|
||||||
_console.ScrollToCaret();
|
_console.ScrollToCaret();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
System.Windows.Forms.Application.DoEvents();
|
|
||||||
|
|
||||||
var firstVisibleLineAfterAppend = GetFirstVisibleLine(_console);
|
var firstVisibleLineAfterAppend = GetFirstVisibleLine(_console);
|
||||||
var linesToRestore = firstVisibleLineBeforeAppend - firstVisibleLineAfterAppend;
|
var linesToRestore = firstVisibleLineBeforeAppend - firstVisibleLineAfterAppend;
|
||||||
@@ -227,7 +217,6 @@ namespace MonsterMonitor.UI
|
|||||||
{
|
{
|
||||||
SendMessage(_console.Handle, EmLineScroll, IntPtr.Zero, (IntPtr)linesToRestore);
|
SendMessage(_console.Handle, EmLineScroll, IntPtr.Zero, (IntPtr)linesToRestore);
|
||||||
}
|
}
|
||||||
System.Windows.Forms.Application.DoEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsConsoleNearBottom()
|
private bool IsConsoleNearBottom()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
@@ -64,7 +65,7 @@ namespace MonsterMonitor.UI
|
|||||||
AddRow(panel, string.Empty, _chkSavePassword, 4);
|
AddRow(panel, string.Empty, _chkSavePassword, 4);
|
||||||
AddRow(panel, "Удаленный порт:", _numRemotePort, 5);
|
AddRow(panel, "Удаленный порт:", _numRemotePort, 5);
|
||||||
AddRow(panel, "Локальный порт:", _numLocalPort, 6);
|
AddRow(panel, "Локальный порт:", _numLocalPort, 6);
|
||||||
AddRow(panel, "Макс. потерь:", _numMaxFailures, 7);
|
AddRow(panel, "Молчание heartbeat (сек):", _numMaxFailures, 7);
|
||||||
AddRow(panel, "Таймаут reconnect (сек):", _numReconnectTimeout, 8);
|
AddRow(panel, "Таймаут reconnect (сек):", _numReconnectTimeout, 8);
|
||||||
AddRow(panel, "Прокси (http://host:port):", _txtProxy, 9);
|
AddRow(panel, "Прокси (http://host:port):", _txtProxy, 9);
|
||||||
AddRow(panel, "Путь к ss:", _txtSsPath, 10);
|
AddRow(panel, "Путь к ss:", _txtSsPath, 10);
|
||||||
@@ -78,8 +79,8 @@ namespace MonsterMonitor.UI
|
|||||||
num.Maximum = 65535;
|
num.Maximum = 65535;
|
||||||
}
|
}
|
||||||
|
|
||||||
_numMaxFailures.Minimum = 1;
|
_numMaxFailures.Minimum = 10;
|
||||||
_numMaxFailures.Maximum = 10;
|
_numMaxFailures.Maximum = 600;
|
||||||
_numReconnectTimeout.Minimum = 5;
|
_numReconnectTimeout.Minimum = 5;
|
||||||
_numReconnectTimeout.Maximum = 60;
|
_numReconnectTimeout.Maximum = 60;
|
||||||
|
|
||||||
@@ -200,7 +201,7 @@ namespace MonsterMonitor.UI
|
|||||||
_chkSavePassword.Checked = _settings.SavePassword;
|
_chkSavePassword.Checked = _settings.SavePassword;
|
||||||
_numRemotePort.Value = _settings.RemotePort;
|
_numRemotePort.Value = _settings.RemotePort;
|
||||||
_numLocalPort.Value = _settings.LocalPort;
|
_numLocalPort.Value = _settings.LocalPort;
|
||||||
_numMaxFailures.Value = _settings.MaxPingFailures;
|
_numMaxFailures.Value = Math.Min(600, Math.Max(10, _settings.HeartbeatSilenceSec));
|
||||||
_numReconnectTimeout.Value = _settings.ReconnectTimeoutSec;
|
_numReconnectTimeout.Value = _settings.ReconnectTimeoutSec;
|
||||||
_txtProxy.Text = _settings.Proxy;
|
_txtProxy.Text = _settings.Proxy;
|
||||||
_txtSsPath.Text = _settings.SsProcessPath;
|
_txtSsPath.Text = _settings.SsProcessPath;
|
||||||
@@ -224,7 +225,7 @@ namespace MonsterMonitor.UI
|
|||||||
_settings.SetPassword(_txtPassword.Text);
|
_settings.SetPassword(_txtPassword.Text);
|
||||||
_settings.RemotePort = (int)_numRemotePort.Value;
|
_settings.RemotePort = (int)_numRemotePort.Value;
|
||||||
_settings.LocalPort = (int)_numLocalPort.Value;
|
_settings.LocalPort = (int)_numLocalPort.Value;
|
||||||
_settings.MaxPingFailures = (int)_numMaxFailures.Value;
|
_settings.HeartbeatSilenceSec = (int)_numMaxFailures.Value;
|
||||||
_settings.ReconnectTimeoutSec = (int)_numReconnectTimeout.Value;
|
_settings.ReconnectTimeoutSec = (int)_numReconnectTimeout.Value;
|
||||||
_settings.Proxy = _txtProxy.Text.Trim();
|
_settings.Proxy = _txtProxy.Text.Trim();
|
||||||
_settings.SsProcessPath = string.IsNullOrWhiteSpace(_txtSsPath.Text)
|
_settings.SsProcessPath = string.IsNullOrWhiteSpace(_txtSsPath.Text)
|
||||||
|
|||||||
Reference in New Issue
Block a user