[feat] 新增内网外网网络质量监测,基于到网关和星尘服务器的Ping丢包和延迟来综合平分
This commit is contained in:
parent
c122afb968
commit
ab159e3507
|
@ -156,6 +156,8 @@
|
|||
<Column Name="Signal" DataType="Int32" Description="信号。信号强度,WiFi/4G" />
|
||||
<Column Name="UplinkSpeed" DataType="Int64" ItemType="GMK" Description="上行速度。网络发送速度,字节每秒" />
|
||||
<Column Name="DownlinkSpeed" DataType="Int64" ItemType="GMK" Description="下行速度。网络接收速度,字节每秒" />
|
||||
<Column Name="IntranetScore" DataType="Double" ItemType="percent" Description="内网质量。综合评估到网关的心跳延迟和丢包率,满分1分" />
|
||||
<Column Name="InternetScore" DataType="Double" ItemType="percent" Description="外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分" />
|
||||
<Column Name="ProcessCount" DataType="Int32" Description="进程数" />
|
||||
<Column Name="TcpConnections" DataType="Int32" Description="连接数。传输数据Established的Tcp网络连接数" />
|
||||
<Column Name="TcpTimeWait" DataType="Int32" Description="主动关闭。主动关闭后TimeWait的Tcp网络连接数,下一步Closed" />
|
||||
|
@ -221,6 +223,8 @@
|
|||
<Column Name="TcpConnections" DataType="Int32" Description="连接数。传输数据Established的Tcp网络连接数" />
|
||||
<Column Name="TcpTimeWait" DataType="Int32" Description="主动关闭。主动关闭后TimeWait的Tcp网络连接数,等待2MSL,确保四次挥手的最后一个ACK能够发出,下一步Closed" />
|
||||
<Column Name="TcpCloseWait" DataType="Int32" Description="被动关闭。作为客户端,收到服务器FIN后进入CloseWait的Tcp网络连接数,还没发送自己的FIN,主要原因是我方太忙" />
|
||||
<Column Name="IntranetScore" DataType="Double" ItemType="percent" Description="内网质量。综合评估到网关的心跳延迟和丢包率,满分1分" />
|
||||
<Column Name="InternetScore" DataType="Double" ItemType="percent" Description="外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分" />
|
||||
<Column Name="Delay" DataType="Int32" Description="延迟。网络延迟,客户端最近一次心跳耗时的一半,单位ms" />
|
||||
<Column Name="Offset" DataType="Int32" Description="偏移。客户端UTC时间加上一半延迟再减服务端UTC时间,单位ms" />
|
||||
<Column Name="LocalTime" DataType="DateTime" Description="本地时间" />
|
||||
|
|
|
@ -1155,6 +1155,28 @@
|
|||
<td>网络接收速度,字节每秒</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>IntranetScore</td>
|
||||
<td>内网质量</td>
|
||||
<td>Double</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>N</td>
|
||||
<td>综合评估到网关的心跳延迟和丢包率,满分1分</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>InternetScore</td>
|
||||
<td>外网质量</td>
|
||||
<td>Double</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>N</td>
|
||||
<td>综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>ProcessCount</td>
|
||||
<td>进程数</td>
|
||||
|
@ -1706,6 +1728,28 @@
|
|||
<td>作为客户端,收到服务器FIN后进入CloseWait的Tcp网络连接数,还没发送自己的FIN,主要原因是我方太忙</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>IntranetScore</td>
|
||||
<td>内网质量</td>
|
||||
<td>Double</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>N</td>
|
||||
<td>综合评估到网关的心跳延迟和丢包率,满分1分</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>InternetScore</td>
|
||||
<td>外网质量</td>
|
||||
<td>Double</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>N</td>
|
||||
<td>综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Delay</td>
|
||||
<td>延迟</td>
|
||||
|
|
|
@ -268,6 +268,8 @@ public partial class NodeOnline : Entity<NodeOnline>
|
|||
if (inf.TcpConnections > 0) online.TcpConnections = inf.TcpConnections;
|
||||
if (inf.TcpTimeWait > 0) online.TcpTimeWait = inf.TcpTimeWait;
|
||||
if (inf.TcpCloseWait > 0) online.TcpCloseWait = inf.TcpCloseWait;
|
||||
if (inf.IntranetScore > 0) online.IntranetScore = inf.IntranetScore;
|
||||
if (inf.InternetScore > 0) online.InternetScore = inf.InternetScore;
|
||||
if (inf.Uptime > 0) online.Uptime = inf.Uptime;
|
||||
if (inf.Delay > 0) online.Delay = inf.Delay;
|
||||
|
||||
|
@ -314,6 +316,8 @@ public partial class NodeOnline : Entity<NodeOnline>
|
|||
TcpConnections = inf.TcpConnections,
|
||||
TcpTimeWait = inf.TcpTimeWait,
|
||||
TcpCloseWait = inf.TcpCloseWait,
|
||||
IntranetScore = inf.IntranetScore,
|
||||
InternetScore = inf.InternetScore,
|
||||
Uptime = inf.Uptime,
|
||||
Delay = inf.Delay,
|
||||
LocalTime = dt,
|
||||
|
|
|
@ -275,6 +275,22 @@ public partial class NodeOnline
|
|||
[BindColumn("DownlinkSpeed", "下行速度。网络接收速度,字节每秒", "", ItemType = "GMK")]
|
||||
public Int64 DownlinkSpeed { get => _DownlinkSpeed; set { if (OnPropertyChanging("DownlinkSpeed", value)) { _DownlinkSpeed = value; OnPropertyChanged("DownlinkSpeed"); } } }
|
||||
|
||||
private Double _IntranetScore;
|
||||
/// <summary>内网质量。综合评估到网关的心跳延迟和丢包率,满分1分</summary>
|
||||
[DisplayName("内网质量")]
|
||||
[Description("内网质量。综合评估到网关的心跳延迟和丢包率,满分1分")]
|
||||
[DataObjectField(false, false, false, 0)]
|
||||
[BindColumn("IntranetScore", "内网质量。综合评估到网关的心跳延迟和丢包率,满分1分", "", ItemType = "percent")]
|
||||
public Double IntranetScore { get => _IntranetScore; set { if (OnPropertyChanging("IntranetScore", value)) { _IntranetScore = value; OnPropertyChanged("IntranetScore"); } } }
|
||||
|
||||
private Double _InternetScore;
|
||||
/// <summary>外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分</summary>
|
||||
[DisplayName("外网质量")]
|
||||
[Description("外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分")]
|
||||
[DataObjectField(false, false, false, 0)]
|
||||
[BindColumn("InternetScore", "外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分", "", ItemType = "percent")]
|
||||
public Double InternetScore { get => _InternetScore; set { if (OnPropertyChanging("InternetScore", value)) { _InternetScore = value; OnPropertyChanged("InternetScore"); } } }
|
||||
|
||||
private Int32 _ProcessCount;
|
||||
/// <summary>进程数</summary>
|
||||
[DisplayName("进程数")]
|
||||
|
@ -465,6 +481,8 @@ public partial class NodeOnline
|
|||
"Signal" => _Signal,
|
||||
"UplinkSpeed" => _UplinkSpeed,
|
||||
"DownlinkSpeed" => _DownlinkSpeed,
|
||||
"IntranetScore" => _IntranetScore,
|
||||
"InternetScore" => _InternetScore,
|
||||
"ProcessCount" => _ProcessCount,
|
||||
"TcpConnections" => _TcpConnections,
|
||||
"TcpTimeWait" => _TcpTimeWait,
|
||||
|
@ -520,6 +538,8 @@ public partial class NodeOnline
|
|||
case "Signal": _Signal = value.ToInt(); break;
|
||||
case "UplinkSpeed": _UplinkSpeed = value.ToLong(); break;
|
||||
case "DownlinkSpeed": _DownlinkSpeed = value.ToLong(); break;
|
||||
case "IntranetScore": _IntranetScore = value.ToDouble(); break;
|
||||
case "InternetScore": _InternetScore = value.ToDouble(); break;
|
||||
case "ProcessCount": _ProcessCount = value.ToInt(); break;
|
||||
case "TcpConnections": _TcpConnections = value.ToInt(); break;
|
||||
case "TcpTimeWait": _TcpTimeWait = value.ToInt(); break;
|
||||
|
@ -674,6 +694,12 @@ public partial class NodeOnline
|
|||
/// <summary>下行速度。网络接收速度,字节每秒</summary>
|
||||
public static readonly Field DownlinkSpeed = FindByName("DownlinkSpeed");
|
||||
|
||||
/// <summary>内网质量。综合评估到网关的心跳延迟和丢包率,满分1分</summary>
|
||||
public static readonly Field IntranetScore = FindByName("IntranetScore");
|
||||
|
||||
/// <summary>外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分</summary>
|
||||
public static readonly Field InternetScore = FindByName("InternetScore");
|
||||
|
||||
/// <summary>进程数</summary>
|
||||
public static readonly Field ProcessCount = FindByName("ProcessCount");
|
||||
|
||||
|
@ -827,6 +853,12 @@ public partial class NodeOnline
|
|||
/// <summary>下行速度。网络接收速度,字节每秒</summary>
|
||||
public const String DownlinkSpeed = "DownlinkSpeed";
|
||||
|
||||
/// <summary>内网质量。综合评估到网关的心跳延迟和丢包率,满分1分</summary>
|
||||
public const String IntranetScore = "IntranetScore";
|
||||
|
||||
/// <summary>外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分</summary>
|
||||
public const String InternetScore = "InternetScore";
|
||||
|
||||
/// <summary>进程数</summary>
|
||||
public const String ProcessCount = "ProcessCount";
|
||||
|
||||
|
|
|
@ -142,6 +142,22 @@ public partial class NodeData
|
|||
[BindColumn("TcpCloseWait", "被动关闭。作为客户端,收到服务器FIN后进入CloseWait的Tcp网络连接数,还没发送自己的FIN,主要原因是我方太忙", "")]
|
||||
public Int32 TcpCloseWait { get => _TcpCloseWait; set { if (OnPropertyChanging("TcpCloseWait", value)) { _TcpCloseWait = value; OnPropertyChanged("TcpCloseWait"); } } }
|
||||
|
||||
private Double _IntranetScore;
|
||||
/// <summary>内网质量。综合评估到网关的心跳延迟和丢包率,满分1分</summary>
|
||||
[DisplayName("内网质量")]
|
||||
[Description("内网质量。综合评估到网关的心跳延迟和丢包率,满分1分")]
|
||||
[DataObjectField(false, false, false, 0)]
|
||||
[BindColumn("IntranetScore", "内网质量。综合评估到网关的心跳延迟和丢包率,满分1分", "", ItemType = "percent")]
|
||||
public Double IntranetScore { get => _IntranetScore; set { if (OnPropertyChanging("IntranetScore", value)) { _IntranetScore = value; OnPropertyChanged("IntranetScore"); } } }
|
||||
|
||||
private Double _InternetScore;
|
||||
/// <summary>外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分</summary>
|
||||
[DisplayName("外网质量")]
|
||||
[Description("外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分")]
|
||||
[DataObjectField(false, false, false, 0)]
|
||||
[BindColumn("InternetScore", "外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分", "", ItemType = "percent")]
|
||||
public Double InternetScore { get => _InternetScore; set { if (OnPropertyChanging("InternetScore", value)) { _InternetScore = value; OnPropertyChanged("InternetScore"); } } }
|
||||
|
||||
private Int32 _Delay;
|
||||
/// <summary>延迟。网络延迟,客户端最近一次心跳耗时的一半,单位ms</summary>
|
||||
[DisplayName("延迟")]
|
||||
|
@ -225,6 +241,8 @@ public partial class NodeData
|
|||
"TcpConnections" => _TcpConnections,
|
||||
"TcpTimeWait" => _TcpTimeWait,
|
||||
"TcpCloseWait" => _TcpCloseWait,
|
||||
"IntranetScore" => _IntranetScore,
|
||||
"InternetScore" => _InternetScore,
|
||||
"Delay" => _Delay,
|
||||
"Offset" => _Offset,
|
||||
"LocalTime" => _LocalTime,
|
||||
|
@ -253,6 +271,8 @@ public partial class NodeData
|
|||
case "TcpConnections": _TcpConnections = value.ToInt(); break;
|
||||
case "TcpTimeWait": _TcpTimeWait = value.ToInt(); break;
|
||||
case "TcpCloseWait": _TcpCloseWait = value.ToInt(); break;
|
||||
case "IntranetScore": _IntranetScore = value.ToDouble(); break;
|
||||
case "InternetScore": _InternetScore = value.ToDouble(); break;
|
||||
case "Delay": _Delay = value.ToInt(); break;
|
||||
case "Offset": _Offset = value.ToInt(); break;
|
||||
case "LocalTime": _LocalTime = value.ToDateTime(); break;
|
||||
|
@ -341,6 +361,12 @@ public partial class NodeData
|
|||
/// <summary>被动关闭。作为客户端,收到服务器FIN后进入CloseWait的Tcp网络连接数,还没发送自己的FIN,主要原因是我方太忙</summary>
|
||||
public static readonly Field TcpCloseWait = FindByName("TcpCloseWait");
|
||||
|
||||
/// <summary>内网质量。综合评估到网关的心跳延迟和丢包率,满分1分</summary>
|
||||
public static readonly Field IntranetScore = FindByName("IntranetScore");
|
||||
|
||||
/// <summary>外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分</summary>
|
||||
public static readonly Field InternetScore = FindByName("InternetScore");
|
||||
|
||||
/// <summary>延迟。网络延迟,客户端最近一次心跳耗时的一半,单位ms</summary>
|
||||
public static readonly Field Delay = FindByName("Delay");
|
||||
|
||||
|
@ -413,6 +439,12 @@ public partial class NodeData
|
|||
/// <summary>被动关闭。作为客户端,收到服务器FIN后进入CloseWait的Tcp网络连接数,还没发送自己的FIN,主要原因是我方太忙</summary>
|
||||
public const String TcpCloseWait = "TcpCloseWait";
|
||||
|
||||
/// <summary>内网质量。综合评估到网关的心跳延迟和丢包率,满分1分</summary>
|
||||
public const String IntranetScore = "IntranetScore";
|
||||
|
||||
/// <summary>外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分</summary>
|
||||
public const String InternetScore = "InternetScore";
|
||||
|
||||
/// <summary>延迟。网络延迟,客户端最近一次心跳耗时的一半,单位ms</summary>
|
||||
public const String Delay = "Delay";
|
||||
|
||||
|
|
|
@ -104,16 +104,20 @@ public class NodeDataController : ReadOnlyEntityController<NodeData>
|
|||
var line = chart.Add(list2, _.TcpConnections);
|
||||
line.YAxisIndex = 1;
|
||||
}
|
||||
if (list2.Any(e => e.TcpTimeWait > 0))
|
||||
{
|
||||
var line = chart.Add(list2, _.TcpTimeWait);
|
||||
line.YAxisIndex = 1;
|
||||
}
|
||||
if (list2.Any(e => e.TcpCloseWait > 0))
|
||||
{
|
||||
var line = chart.Add(list2, _.TcpCloseWait);
|
||||
line.YAxisIndex = 1;
|
||||
}
|
||||
//if (list2.Any(e => e.TcpTimeWait > 0))
|
||||
//{
|
||||
// var line = chart.Add(list2, _.TcpTimeWait);
|
||||
// line.YAxisIndex = 1;
|
||||
//}
|
||||
//if (list2.Any(e => e.TcpCloseWait > 0))
|
||||
//{
|
||||
// var line = chart.Add(list2, _.TcpCloseWait);
|
||||
// line.YAxisIndex = 1;
|
||||
//}
|
||||
|
||||
chart.AddLine(list2, _.IntranetScore, e => Math.Round(e.IntranetScore * 100));
|
||||
chart.AddLine(list2, _.InternetScore, e => Math.Round(e.InternetScore * 100));
|
||||
|
||||
//chart.Add(list2, _.Offset);
|
||||
chart.SetTooltip();
|
||||
ViewBag.Charts = new[] { chart };
|
||||
|
|
|
@ -67,6 +67,12 @@ public class PingInfo : PingRequest
|
|||
/// <summary>被动关闭的Tcp连接数</summary>
|
||||
public Int32 TcpCloseWait { get; set; }
|
||||
|
||||
/// <summary>内网质量。综合评估到网关的心跳延迟和丢包率,满分1分</summary>
|
||||
public Double IntranetScore { get; set; }
|
||||
|
||||
/// <summary>外网质量。综合评估到DNS和星尘服务器的心跳延迟和丢包率,满分1分</summary>
|
||||
public Double InternetScore { get; set; }
|
||||
|
||||
///// <summary>开机时间,单位s</summary>
|
||||
//public Int32 Uptime { get; set; }
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
using System.Net.NetworkInformation;
|
||||
using System.Text.RegularExpressions;
|
||||
using NewLife;
|
||||
using NewLife.Log;
|
||||
|
||||
namespace Stardust.Monitors;
|
||||
|
||||
/// <summary>心跳监控。通过发往目标IP的Ping命令来计算延迟和丢包率,进一步得到分数</summary>
|
||||
public class PingMonitor
|
||||
{
|
||||
#region 属性
|
||||
/// <summary>心跳次数</summary>
|
||||
public Int32 Times { get; set; } = 5;
|
||||
|
||||
/// <summary>多次心跳的间隔</summary>
|
||||
public Int32 Interval { get; set; } = 1_000;
|
||||
#endregion
|
||||
|
||||
#region 方法
|
||||
/// <summary>执行多次Ping请求,获取网络质量评分</summary>
|
||||
/// <param name="host"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Double> GetScoreAsync(String? host)
|
||||
{
|
||||
//XTrace.WriteLine("GetScoreAsync:{0}", host);
|
||||
if (host.IsNullOrEmpty()) return 0;
|
||||
|
||||
var rtTimes = new List<Double>();
|
||||
|
||||
using var ping = new Ping();
|
||||
for (var i = 0; i < Times; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var reply = await ping.SendPingAsync(host, 3_000).ConfigureAwait(false);
|
||||
|
||||
if (reply.Status == IPStatus.Success)
|
||||
rtTimes.Add(reply.RoundtripTime);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (i + 1 < Times) await Task.Delay(Interval).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// 综合评估分数
|
||||
if (rtTimes.Count > 0)
|
||||
{
|
||||
// 延迟阈值50ms
|
||||
var threshold = 1f;
|
||||
var successRate = rtTimes.Count / Times;
|
||||
var latency = rtTimes.Average();
|
||||
var latencyScore = 0d;
|
||||
if (latency <= threshold)
|
||||
latencyScore = 1f;
|
||||
else
|
||||
latencyScore = Math.Exp(-0.01 * (latency - threshold)); // 衰减系数λ=0.2
|
||||
|
||||
// 确保得分在0-1之间
|
||||
var score = successRate * latencyScore;
|
||||
//XTrace.WriteLine($"Score: {score} successRate:{successRate} latency:{latency} latencyScore:{latencyScore}");
|
||||
return Math.Round(score, 3);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endregion
|
||||
}
|
|
@ -9,11 +9,13 @@ using NewLife.Data;
|
|||
using NewLife.Log;
|
||||
using NewLife.Model;
|
||||
using NewLife.Reflection;
|
||||
using NewLife.Remoting;
|
||||
using NewLife.Remoting.Clients;
|
||||
using NewLife.Remoting.Models;
|
||||
using NewLife.Security;
|
||||
using Stardust.Managers;
|
||||
using Stardust.Models;
|
||||
using Stardust.Monitors;
|
||||
|
||||
namespace Stardust;
|
||||
|
||||
|
@ -289,6 +291,16 @@ public class StarClient : ClientBase, ICommandClient, IEventProvider
|
|||
var request = new PingInfo();
|
||||
FillPingRequest(request);
|
||||
|
||||
// 获取网络质量
|
||||
var monitor = new PingMonitor();
|
||||
var gw = AgentInfo.GetGateway();
|
||||
if (gw != null && gw.Contains('/')) gw = gw.Substring(0, gw.IndexOf("/"));
|
||||
var gwtTask = Task.Run(() => monitor.GetScoreAsync(gw));
|
||||
var dns = AgentInfo.GetDns();
|
||||
var dnsTask = Task.Run(() => monitor.GetScoreAsync(dns));
|
||||
var svr = (Client as ApiHttpClient)?.Current?.Address.Host;
|
||||
var svrTask = Task.Run(() => monitor.GetScoreAsync(svr));
|
||||
|
||||
var exs = _excludes.Where(e => e.Contains('*')).ToArray();
|
||||
|
||||
var ps = Process.GetProcesses();
|
||||
|
@ -336,6 +348,10 @@ public class StarClient : ClientBase, ICommandClient, IEventProvider
|
|||
}
|
||||
catch { }
|
||||
|
||||
// 获取网络质量
|
||||
request.IntranetScore = gwtTask?.Result ?? 0;
|
||||
request.InternetScore = (dnsTask?.Result ?? 0) * 0.3 + (svrTask?.Result ?? 0) * 0.7;
|
||||
|
||||
if (mi is IExtend ext)
|
||||
{
|
||||
// 读取无线信号强度
|
||||
|
|
Loading…
Reference in New Issue