A:增加窗口网速展示
This commit is contained in:
1 parent
d484c85abb
commit
c1d9e2c068
28 files changed
+2107
-479
No files matched your search
@@ -1,6 +1,6 @@
|
||||
using Serilog;
|
||||
using Common.Logger;
|
||||
|
||||
namespace Common.FunExpand;
|
||||
namespace Common.ExpandFun;
|
||||
|
||||
public static class StringExpand
|
||||
{
|
||||
@@ -61,4 +61,4 @@ public static class StringExpand
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading;
|
||||
using Common.Logger;
|
||||
|
||||
namespace Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础网络监控帮助类 - 专注于网速计算
|
||||
/// </summary>
|
||||
public static class BasicNetworkMonitor
|
||||
{
|
||||
#region 数据模型
|
||||
|
||||
/// <summary>
|
||||
/// 基础网络接口信息
|
||||
/// </summary>
|
||||
public class BasicNetworkInterface
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public long BytesReceived { get; set; }
|
||||
public long BytesSent { get; set; }
|
||||
public bool IsConnected { get; set; }
|
||||
public NetworkInterfaceType Type { get; set; }
|
||||
public string TypeDescription { get; set; } = string.Empty;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网络统计快照
|
||||
/// </summary>
|
||||
private class NetworkSnapshot
|
||||
{
|
||||
public long BytesReceived { get; set; }
|
||||
public long BytesSent { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有字段
|
||||
|
||||
// 使用线程安全的字典存储历史统计数据
|
||||
private static readonly ConcurrentDictionary<string, NetworkSnapshot> PreviousStats = new();
|
||||
private static readonly object LockObject = new();
|
||||
private static volatile bool _isInitialized = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 公共方法
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有网络接口
|
||||
/// </summary>
|
||||
/// <param name="includeLoopback">是否包含环回接口</param>
|
||||
/// <returns>网络接口列表</returns>
|
||||
public static List<BasicNetworkInterface> GetNetworkInterfaces(bool includeLoopback = false)
|
||||
{
|
||||
var interfaces = new List<BasicNetworkInterface>();
|
||||
|
||||
try
|
||||
{
|
||||
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
foreach (var ni in networkInterfaces)
|
||||
{
|
||||
// 根据参数决定是否跳过环回接口
|
||||
if (!includeLoopback && ni.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var basicInterface = CreateBasicInterface(ni);
|
||||
if (basicInterface != null)
|
||||
{
|
||||
interfaces.Add(basicInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error4Ctx($"获取网络接口时发生错误: {ex.Message}");
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据接口名称计算网速
|
||||
/// </summary>
|
||||
/// <param name="interfaceName">接口名称</param>
|
||||
/// <returns>网速信息</returns>
|
||||
public static NetworkSpeed CalculateSpeedByName(string interfaceName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(interfaceName))
|
||||
{
|
||||
Log.Warning("接口名称不能为空");
|
||||
return new NetworkSpeed();
|
||||
}
|
||||
|
||||
var interfaces = GetNetworkInterfaces();
|
||||
var targetInterface = interfaces.FirstOrDefault(i =>
|
||||
string.Equals(i.Name, interfaceName, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(i.Id, interfaceName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (targetInterface == null)
|
||||
{
|
||||
Log.Warning($"未找到接口: {interfaceName}");
|
||||
return new NetworkSpeed();
|
||||
}
|
||||
|
||||
return CalculateSpeed(targetInterface);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据接口ID计算网速
|
||||
/// </summary>
|
||||
/// <param name="interfaceId">接口ID</param>
|
||||
/// <returns>网速信息</returns>
|
||||
public static NetworkSpeed CalculateSpeedById(string interfaceId)
|
||||
{
|
||||
return CalculateSpeedByName(interfaceId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算所有网络接口的总网速
|
||||
/// </summary>
|
||||
/// <returns>总网速信息</returns>
|
||||
public static NetworkSpeed CalculateTotalSpeed()
|
||||
{
|
||||
var currentInterfaces = GetNetworkInterfaces();
|
||||
var now = DateTime.Now;
|
||||
var totalSpeed = new NetworkSpeed();
|
||||
|
||||
// 确保初始化
|
||||
EnsureInitialized(currentInterfaces, now);
|
||||
|
||||
foreach (var currentInterface in currentInterfaces)
|
||||
{
|
||||
var speed = CalculateSpeed(currentInterface, now);
|
||||
totalSpeed.DownloadSpeed += speed.DownloadSpeed;
|
||||
totalSpeed.UploadSpeed += speed.UploadSpeed;
|
||||
}
|
||||
|
||||
Log.Info($"总网速 - 下载: {totalSpeed.DownloadSpeedText}, 上传: {totalSpeed.UploadSpeedText}");
|
||||
return totalSpeed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置统计数据
|
||||
/// </summary>
|
||||
public static void ResetStatistics()
|
||||
{
|
||||
lock (LockObject)
|
||||
{
|
||||
Log.Info("重置网络统计数据");
|
||||
PreviousStats.Clear();
|
||||
_isInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取接口的历史统计数据
|
||||
/// </summary>
|
||||
/// <param name="interfaceId">接口ID</param>
|
||||
/// <returns>是否存在历史数据</returns>
|
||||
public static bool HasHistoricalData(string interfaceId)
|
||||
{
|
||||
return !string.IsNullOrEmpty(interfaceId) && PreviousStats.ContainsKey(interfaceId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取已连接的网络接口
|
||||
/// </summary>
|
||||
/// <returns>已连接的网络接口列表</returns>
|
||||
public static List<BasicNetworkInterface> GetConnectedNetworkInterfaces()
|
||||
{
|
||||
return GetNetworkInterfaces()
|
||||
.Where(i => i.IsConnected)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
||||
/// <summary>
|
||||
/// 创建基础网络接口信息
|
||||
/// </summary>
|
||||
/// <param name="ni">网络接口</param>
|
||||
/// <returns>基础网络接口信息</returns>
|
||||
private static BasicNetworkInterface CreateBasicInterface(NetworkInterface ni)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取IPv4统计信息
|
||||
var stats = ni.GetIPv4Statistics();
|
||||
|
||||
return new BasicNetworkInterface
|
||||
{
|
||||
Name = ni.Name,
|
||||
Description = ni.Description,
|
||||
Id = ni.Id,
|
||||
BytesReceived = stats.BytesReceived,
|
||||
BytesSent = stats.BytesSent,
|
||||
IsConnected = ni.OperationalStatus == OperationalStatus.Up,
|
||||
Type = ni.NetworkInterfaceType,
|
||||
TypeDescription = NetworkHelper.GetInterfaceTypeDescription(ni.NetworkInterfaceType)
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning($"创建接口 {ni.Name} 基础信息时发生错误: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算指定接口的网速
|
||||
/// </summary>
|
||||
/// <param name="currentInterface">当前接口信息</param>
|
||||
/// <param name="now">当前时间(可选)</param>
|
||||
/// <returns>网速信息</returns>
|
||||
private static NetworkSpeed CalculateSpeed(BasicNetworkInterface currentInterface, DateTime? now = null)
|
||||
{
|
||||
if (currentInterface == null)
|
||||
{
|
||||
return new NetworkSpeed();
|
||||
}
|
||||
|
||||
var timestamp = now ?? DateTime.Now;
|
||||
var key = currentInterface.Id;
|
||||
var speed = new NetworkSpeed();
|
||||
|
||||
Log.Debug($"计算接口 {currentInterface.Name} 的网速...");
|
||||
|
||||
// 确保初始化
|
||||
if (!_isInitialized)
|
||||
{
|
||||
EnsureInitialized(new[] { currentInterface }, timestamp);
|
||||
return speed;
|
||||
}
|
||||
|
||||
// 获取或创建前一次的快照
|
||||
var currentSnapshot = new NetworkSnapshot
|
||||
{
|
||||
BytesReceived = currentInterface.BytesReceived,
|
||||
BytesSent = currentInterface.BytesSent,
|
||||
Timestamp = timestamp
|
||||
};
|
||||
|
||||
if (PreviousStats.TryGetValue(key, out var previousSnapshot))
|
||||
{
|
||||
var timeDiff = (timestamp - previousSnapshot.Timestamp).TotalSeconds;
|
||||
|
||||
if (NetworkHelper.IsValidTimeInterval(timeDiff))
|
||||
{
|
||||
// 计算速度
|
||||
speed.DownloadSpeed = NetworkHelper.CalculateSpeed(
|
||||
currentSnapshot.BytesReceived,
|
||||
previousSnapshot.BytesReceived,
|
||||
timeDiff);
|
||||
|
||||
speed.UploadSpeed = NetworkHelper.CalculateSpeed(
|
||||
currentSnapshot.BytesSent,
|
||||
previousSnapshot.BytesSent,
|
||||
timeDiff);
|
||||
|
||||
Log.Debug(
|
||||
$"接口 {currentInterface.Name} - 下载: {speed.DownloadSpeedText}, 上传: {speed.UploadSpeedText}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug($"时间间隔过短 ({timeDiff:F2}秒),跳过速度计算");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug($"接口 {currentInterface.Name} 首次记录数据");
|
||||
}
|
||||
|
||||
// 更新快照
|
||||
PreviousStats.AddOrUpdate(key, currentSnapshot, (k, v) => currentSnapshot);
|
||||
|
||||
return speed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保系统已初始化
|
||||
/// </summary>
|
||||
/// <param name="interfaces">接口列表</param>
|
||||
/// <param name="timestamp">时间戳</param>
|
||||
private static void EnsureInitialized(IEnumerable<BasicNetworkInterface> interfaces, DateTime timestamp)
|
||||
{
|
||||
if (_isInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (LockObject)
|
||||
{
|
||||
if (_isInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info("初始化网络监控统计数据");
|
||||
|
||||
foreach (var iface in interfaces)
|
||||
{
|
||||
if (iface != null)
|
||||
{
|
||||
PreviousStats.TryAdd(iface.Id, new NetworkSnapshot
|
||||
{
|
||||
BytesReceived = iface.BytesReceived,
|
||||
BytesSent = iface.BytesSent,
|
||||
Timestamp = timestamp
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_isInitialized = true;
|
||||
Log.Info($"已初始化 {PreviousStats.Count} 个网络接口的统计数据");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理过期的统计数据
|
||||
/// </summary>
|
||||
/// <param name="maxAge">最大保留时间</param>
|
||||
public static void CleanupOldStatistics(TimeSpan maxAge)
|
||||
{
|
||||
var cutoffTime = DateTime.Now - maxAge;
|
||||
var expiredKeys = new List<string>();
|
||||
|
||||
foreach (var kvp in PreviousStats)
|
||||
{
|
||||
if (kvp.Value.Timestamp < cutoffTime)
|
||||
{
|
||||
expiredKeys.Add(kvp.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var key in expiredKeys)
|
||||
{
|
||||
PreviousStats.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
if (expiredKeys.Count > 0)
|
||||
{
|
||||
Log.Info($"清理了 {expiredKeys.Count} 个过期的网络统计记录");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Common.Logger;
|
||||
|
||||
namespace Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 增强的网络监控帮助类 - 获取详细的网络信息
|
||||
/// </summary>
|
||||
public static class EnhancedNetworkMonitor
|
||||
{
|
||||
#region 数据模型
|
||||
|
||||
/// <summary>
|
||||
/// 详细的网络接口信息
|
||||
/// </summary>
|
||||
public class DetailedNetworkInterface
|
||||
{
|
||||
// 基本信息
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public bool IsConnected { get; set; }
|
||||
public NetworkInterfaceType Type { get; set; }
|
||||
public string TypeDescription { get; set; } = string.Empty;
|
||||
|
||||
// 物理信息
|
||||
public string PhysicalAddress { get; set; } = string.Empty;
|
||||
public long Speed { get; set; }
|
||||
public string SpeedText { get; set; } = string.Empty;
|
||||
|
||||
// IP配置
|
||||
public List<string> IPAddresses { get; set; } = new();
|
||||
public List<string> SubnetMasks { get; set; } = new();
|
||||
public List<string> Gateways { get; set; } = new();
|
||||
public List<string> DnsServers { get; set; } = new();
|
||||
public List<string> IPv6Addresses { get; set; } = new();
|
||||
|
||||
// 统计信息
|
||||
public long BytesReceived { get; set; }
|
||||
public long BytesSent { get; set; }
|
||||
public long PacketsReceived { get; set; }
|
||||
public long PacketsSent { get; set; }
|
||||
public long ErrorsReceived { get; set; }
|
||||
public long ErrorsSent { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name} - {Description}";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 公共方法
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有网络接口的详细信息
|
||||
/// </summary>
|
||||
/// <param name="includeLoopback">是否包含环回接口</param>
|
||||
/// <returns>网络接口列表</returns>
|
||||
public static List<DetailedNetworkInterface> GetDetailedNetworkInterfaces(bool includeLoopback = false)
|
||||
{
|
||||
var interfaces = new List<DetailedNetworkInterface>();
|
||||
|
||||
try
|
||||
{
|
||||
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
foreach (var ni in networkInterfaces)
|
||||
{
|
||||
// 根据参数决定是否跳过环回接口
|
||||
if (!includeLoopback && ni.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var detailedInterface = CreateDetailedInterface(ni);
|
||||
if (detailedInterface != null)
|
||||
{
|
||||
interfaces.Add(detailedInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error4Ctx($"获取网络接口详细信息时发生错误: {ex.Message}");
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取已连接的网络接口
|
||||
/// </summary>
|
||||
/// <returns>已连接的网络接口列表</returns>
|
||||
public static List<DetailedNetworkInterface> GetConnectedNetworkInterfaces()
|
||||
{
|
||||
return GetDetailedNetworkInterfaces()
|
||||
.Where(i => i.IsConnected)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据名称获取网络接口
|
||||
/// </summary>
|
||||
/// <param name="name">接口名称</param>
|
||||
/// <returns>网络接口信息,未找到则返回null</returns>
|
||||
public static DetailedNetworkInterface GetNetworkInterfaceByName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return GetDetailedNetworkInterfaces()
|
||||
.FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取外网IP地址
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>外网IP信息</returns>
|
||||
public static async Task<ExternalIPInfo> GetExternalIPAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await NetworkHelper.GetExternalIPAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前主要网络接口的详细信息
|
||||
/// </summary>
|
||||
/// <returns>主要网络接口信息,如果没有则返回null</returns>
|
||||
public static DetailedNetworkInterface GetPrimaryNetworkInterface()
|
||||
{
|
||||
var connectedInterfaces = GetConnectedNetworkInterfaces();
|
||||
|
||||
// 优先选择有网关的以太网接口
|
||||
var primaryInterface = connectedInterfaces
|
||||
.Where(i => i.Type == NetworkInterfaceType.Ethernet && i.Gateways.Any())
|
||||
.FirstOrDefault();
|
||||
|
||||
// 如果没有以太网,选择有网关的Wi-Fi接口
|
||||
if (primaryInterface == null)
|
||||
{
|
||||
primaryInterface = connectedInterfaces
|
||||
.Where(i => i.Type == NetworkInterfaceType.Wireless80211 && i.Gateways.Any())
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
// 如果还没有,选择任何有IP地址的接口
|
||||
if (primaryInterface == null)
|
||||
{
|
||||
primaryInterface = connectedInterfaces
|
||||
.Where(i => i.IPAddresses.Any())
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
return primaryInterface;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取完整的网络状态报告
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>网络状态报告</returns>
|
||||
public static async Task<string> GetNetworkStatusReportAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
try
|
||||
{
|
||||
var interfaces = GetConnectedNetworkInterfaces();
|
||||
var externalIP = await GetExternalIPAsync(cancellationToken);
|
||||
var uptime = NetworkHelper.GetSystemUptime();
|
||||
|
||||
// 报告头部
|
||||
sb.AppendLine("=== 网络状态详细报告 ===");
|
||||
sb.AppendLine($"报告生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||
sb.AppendLine($"系统运行时间: {NetworkHelper.FormatUptime(uptime)}");
|
||||
sb.AppendLine($"外网IPv4地址: {externalIP.IPv4}");
|
||||
sb.AppendLine($"外网IPv6地址: {externalIP.IPv6}");
|
||||
sb.AppendLine($"活跃网络接口数: {interfaces.Count}");
|
||||
sb.AppendLine();
|
||||
|
||||
// 接口详情
|
||||
foreach (var iface in interfaces)
|
||||
{
|
||||
AppendInterfaceInfo(sb, iface);
|
||||
}
|
||||
|
||||
// 统计汇总
|
||||
AppendNetworkSummary(sb, interfaces);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error4Ctx($"生成网络状态报告时发生错误: {ex.Message}");
|
||||
sb.AppendLine($"生成报告时发生错误: {ex.Message}");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
||||
/// <summary>
|
||||
/// 创建详细的网络接口信息
|
||||
/// </summary>
|
||||
/// <param name="ni">网络接口</param>
|
||||
/// <returns>详细网络接口信息</returns>
|
||||
private static DetailedNetworkInterface CreateDetailedInterface(NetworkInterface ni)
|
||||
{
|
||||
try
|
||||
{
|
||||
var detailedInterface = new DetailedNetworkInterface
|
||||
{
|
||||
// 基本信息
|
||||
Name = ni.Name,
|
||||
Description = ni.Description,
|
||||
Id = ni.Id,
|
||||
IsConnected = ni.OperationalStatus == OperationalStatus.Up,
|
||||
Type = ni.NetworkInterfaceType,
|
||||
TypeDescription = NetworkHelper.GetInterfaceTypeDescription(ni.NetworkInterfaceType),
|
||||
|
||||
// 物理信息
|
||||
PhysicalAddress = NetworkHelper.FormatMacAddress(ni.GetPhysicalAddress()),
|
||||
Speed = ni.Speed,
|
||||
SpeedText = NetworkHelper.FormatInterfaceSpeed(ni.Speed)
|
||||
};
|
||||
|
||||
// 获取统计信息
|
||||
PopulateStatistics(detailedInterface, ni);
|
||||
|
||||
// 获取IP配置信息
|
||||
PopulateIPConfiguration(detailedInterface, ni);
|
||||
|
||||
return detailedInterface;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning($"创建接口 {ni.Name} 的详细信息时发生错误: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 填充统计信息
|
||||
/// </summary>
|
||||
/// <param name="detailedInterface">详细接口信息</param>
|
||||
/// <param name="ni">网络接口</param>
|
||||
private static void PopulateStatistics(DetailedNetworkInterface detailedInterface, NetworkInterface ni)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stats = ni.GetIPv4Statistics();
|
||||
detailedInterface.BytesReceived = stats.BytesReceived;
|
||||
detailedInterface.BytesSent = stats.BytesSent;
|
||||
detailedInterface.PacketsReceived = stats.UnicastPacketsReceived;
|
||||
detailedInterface.PacketsSent = stats.UnicastPacketsSent;
|
||||
detailedInterface.ErrorsReceived = stats.IncomingPacketsWithErrors;
|
||||
detailedInterface.ErrorsSent = stats.OutgoingPacketsWithErrors;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning($"获取接口 {ni.Name} 统计信息时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 填充IP配置信息
|
||||
/// </summary>
|
||||
/// <param name="detailedInterface">详细接口信息</param>
|
||||
/// <param name="ni">网络接口</param>
|
||||
private static void PopulateIPConfiguration(DetailedNetworkInterface detailedInterface, NetworkInterface ni)
|
||||
{
|
||||
try
|
||||
{
|
||||
var ipProps = ni.GetIPProperties();
|
||||
|
||||
// 处理单播地址
|
||||
foreach (var unicast in ipProps.UnicastAddresses)
|
||||
{
|
||||
switch (unicast.Address.AddressFamily)
|
||||
{
|
||||
case AddressFamily.InterNetwork:
|
||||
detailedInterface.IPAddresses.Add(unicast.Address.ToString());
|
||||
var mask = NetworkHelper.CalculateSubnetMask(unicast);
|
||||
if (!string.IsNullOrEmpty(mask))
|
||||
{
|
||||
detailedInterface.SubnetMasks.Add(mask);
|
||||
}
|
||||
|
||||
break;
|
||||
case AddressFamily.InterNetworkV6:
|
||||
detailedInterface.IPv6Addresses.Add(unicast.Address.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理网关
|
||||
foreach (var gateway in ipProps.GatewayAddresses)
|
||||
{
|
||||
if (gateway.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
detailedInterface.Gateways.Add(gateway.Address.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// 处理DNS服务器
|
||||
foreach (var dns in ipProps.DnsAddresses)
|
||||
{
|
||||
if (dns.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
detailedInterface.DnsServers.Add(dns.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning($"获取接口 {ni.Name} IP配置时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加接口信息到报告
|
||||
/// </summary>
|
||||
/// <param name="sb">字符串构建器</param>
|
||||
/// <param name="iface">接口信息</param>
|
||||
private static void AppendInterfaceInfo(StringBuilder sb, DetailedNetworkInterface iface)
|
||||
{
|
||||
sb.AppendLine($"接口名称: {iface.Name}");
|
||||
sb.AppendLine($"接口描述: {iface.Description}");
|
||||
sb.AppendLine($"连接类型: {iface.TypeDescription}");
|
||||
sb.AppendLine($"接口速度: {iface.SpeedText}");
|
||||
sb.AppendLine($"物理地址: {iface.PhysicalAddress}");
|
||||
sb.AppendLine($"连接状态: {(iface.IsConnected ? "已连接" : "未连接")}");
|
||||
|
||||
if (iface.IPAddresses.Any())
|
||||
{
|
||||
sb.AppendLine($"IPv4地址: {string.Join(", ", iface.IPAddresses)}");
|
||||
}
|
||||
|
||||
if (iface.SubnetMasks.Any())
|
||||
{
|
||||
sb.AppendLine($"子网掩码: {string.Join(", ", iface.SubnetMasks)}");
|
||||
}
|
||||
|
||||
if (iface.Gateways.Any())
|
||||
{
|
||||
sb.AppendLine($"默认网关: {string.Join(", ", iface.Gateways)}");
|
||||
}
|
||||
|
||||
if (iface.DnsServers.Any())
|
||||
{
|
||||
sb.AppendLine($"DNS服务器: {string.Join(", ", iface.DnsServers)}");
|
||||
}
|
||||
|
||||
if (iface.IPv6Addresses.Any())
|
||||
{
|
||||
sb.AppendLine($"IPv6地址: {string.Join(", ", iface.IPv6Addresses)}");
|
||||
}
|
||||
|
||||
sb.AppendLine($"已接收: {NetworkHelper.FormatBytes(iface.BytesReceived)} ({iface.PacketsReceived:N0} 包)");
|
||||
sb.AppendLine($"已发送: {NetworkHelper.FormatBytes(iface.BytesSent)} ({iface.PacketsSent:N0} 包)");
|
||||
|
||||
if (iface.ErrorsReceived > 0 || iface.ErrorsSent > 0)
|
||||
{
|
||||
sb.AppendLine($"错误统计: 接收错误 {iface.ErrorsReceived:N0}, 发送错误 {iface.ErrorsSent:N0}");
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加网络汇总信息
|
||||
/// </summary>
|
||||
/// <param name="sb">字符串构建器</param>
|
||||
/// <param name="interfaces">接口列表</param>
|
||||
private static void AppendNetworkSummary(StringBuilder sb, List<DetailedNetworkInterface> interfaces)
|
||||
{
|
||||
if (!interfaces.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sb.AppendLine("=== 网络统计汇总 ===");
|
||||
|
||||
var totalBytesReceived = interfaces.Sum(i => i.BytesReceived);
|
||||
var totalBytesSent = interfaces.Sum(i => i.BytesSent);
|
||||
var totalPacketsReceived = interfaces.Sum(i => i.PacketsReceived);
|
||||
var totalPacketsSent = interfaces.Sum(i => i.PacketsSent);
|
||||
var totalErrors = interfaces.Sum(i => i.ErrorsReceived + i.ErrorsSent);
|
||||
|
||||
sb.AppendLine($"总接收流量: {NetworkHelper.FormatBytes(totalBytesReceived)}");
|
||||
sb.AppendLine($"总发送流量: {NetworkHelper.FormatBytes(totalBytesSent)}");
|
||||
sb.AppendLine($"总数据包: 接收 {totalPacketsReceived:N0}, 发送 {totalPacketsSent:N0}");
|
||||
|
||||
if (totalErrors > 0)
|
||||
{
|
||||
sb.AppendLine($"总错误数: {totalErrors:N0}");
|
||||
}
|
||||
|
||||
var ethernetCount = interfaces.Count(i => i.Type == NetworkInterfaceType.Ethernet);
|
||||
var wifiCount = interfaces.Count(i => i.Type == NetworkInterfaceType.Wireless80211);
|
||||
|
||||
sb.AppendLine(
|
||||
$"接口类型分布: 以太网 {ethernetCount}, Wi-Fi {wifiCount}, 其他 {interfaces.Count - ethernetCount - wifiCount}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Common.Logger;
|
||||
|
||||
namespace Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络工具公共类 - 提供格式化、验证等公共功能
|
||||
/// </summary>
|
||||
public static class NetworkHelper
|
||||
{
|
||||
#region 常量定义
|
||||
|
||||
private const long MAX_REASONABLE_SPEED = 1_000_000_000; // 1GB/s 最大合理速度
|
||||
private const double MIN_TIME_INTERVAL = 0.5; // 最小时间间隔(秒)
|
||||
private const int MAC_ADDRESS_LENGTH = 12;
|
||||
private const int IPV4_PREFIX_MAX = 32;
|
||||
private const int IPV6_PREFIX_MAX = 128;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 格式化方法
|
||||
|
||||
/// <summary>
|
||||
/// 格式化网速显示
|
||||
/// </summary>
|
||||
/// <param name="bytesPerSecond">每秒字节数</param>
|
||||
/// <returns>格式化后的速度字符串</returns>
|
||||
public static string FormatSpeed(double bytesPerSecond)
|
||||
{
|
||||
if (!IsValidSpeed(bytesPerSecond))
|
||||
{
|
||||
return "0.00 B/s";
|
||||
}
|
||||
|
||||
var units = new[] { "B/s", "KB/s", "MB/s", "GB/s", "TB/s" };
|
||||
var unitIndex = 0;
|
||||
var speed = bytesPerSecond;
|
||||
|
||||
while (speed >= 1024 && unitIndex < units.Length - 1)
|
||||
{
|
||||
speed /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return $"{speed:0.00} {units[unitIndex]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化接口速度显示
|
||||
/// </summary>
|
||||
/// <param name="bitsPerSecond">每秒比特数</param>
|
||||
/// <returns>格式化后的速度字符串</returns>
|
||||
public static string FormatInterfaceSpeed(long bitsPerSecond)
|
||||
{
|
||||
if (bitsPerSecond <= 0)
|
||||
{
|
||||
return "未知";
|
||||
}
|
||||
|
||||
var units = new[] { "bps", "Kbps", "Mbps", "Gbps", "Tbps" };
|
||||
var unitIndex = 0;
|
||||
var speed = (double)bitsPerSecond;
|
||||
|
||||
while (speed >= 1000 && unitIndex < units.Length - 1)
|
||||
{
|
||||
speed /= 1000;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return $"{speed:0.##} {units[unitIndex]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化字节数显示
|
||||
/// </summary>
|
||||
/// <param name="bytes">字节数</param>
|
||||
/// <returns>格式化后的字节数字符串</returns>
|
||||
public static string FormatBytes(long bytes)
|
||||
{
|
||||
if (bytes < 0)
|
||||
{
|
||||
return "0 B";
|
||||
}
|
||||
|
||||
var units = new[] { "B", "KB", "MB", "GB", "TB", "PB" };
|
||||
var unitIndex = 0;
|
||||
var size = (double)bytes;
|
||||
|
||||
while (size >= 1024 && unitIndex < units.Length - 1)
|
||||
{
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return $"{size:0.##} {units[unitIndex]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化MAC地址
|
||||
/// </summary>
|
||||
/// <param name="physicalAddress">物理地址对象</param>
|
||||
/// <param name="separator">分隔符,默认为"-"</param>
|
||||
/// <returns>格式化后的MAC地址</returns>
|
||||
public static string FormatMacAddress(PhysicalAddress physicalAddress, string separator = "-")
|
||||
{
|
||||
if (physicalAddress == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var address = physicalAddress.ToString();
|
||||
if (string.IsNullOrEmpty(address) || address.Length != MAC_ADDRESS_LENGTH)
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
return string.Join(separator,
|
||||
Enumerable.Range(0, 6)
|
||||
.Select(i => address.Substring(i * 2, 2))
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化系统运行时间
|
||||
/// </summary>
|
||||
/// <param name="uptime">运行时间</param>
|
||||
/// <returns>格式化后的运行时间字符串</returns>
|
||||
public static string FormatUptime(TimeSpan uptime)
|
||||
{
|
||||
if (uptime.TotalSeconds < 0)
|
||||
{
|
||||
return "0秒";
|
||||
}
|
||||
|
||||
var parts = new List<string>();
|
||||
|
||||
if (uptime.Days > 0)
|
||||
parts.Add($"{uptime.Days}天");
|
||||
if (uptime.Hours > 0)
|
||||
parts.Add($"{uptime.Hours}小时");
|
||||
if (uptime.Minutes > 0)
|
||||
parts.Add($"{uptime.Minutes}分");
|
||||
if (uptime.Seconds > 0 || parts.Count == 0)
|
||||
parts.Add($"{uptime.Seconds}秒");
|
||||
|
||||
return string.Join("", parts);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 验证方法
|
||||
|
||||
/// <summary>
|
||||
/// 验证网速是否有效
|
||||
/// </summary>
|
||||
/// <param name="speed">网速值</param>
|
||||
/// <returns>是否有效</returns>
|
||||
public static bool IsValidSpeed(double speed)
|
||||
{
|
||||
return speed >= 0 &&
|
||||
!double.IsNaN(speed) &&
|
||||
!double.IsInfinity(speed) &&
|
||||
speed < MAX_REASONABLE_SPEED;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证时间间隔是否足够
|
||||
/// </summary>
|
||||
/// <param name="timeInterval">时间间隔(秒)</param>
|
||||
/// <returns>是否足够</returns>
|
||||
public static bool IsValidTimeInterval(double timeInterval)
|
||||
{
|
||||
return timeInterval >= MIN_TIME_INTERVAL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证字节数变化是否合理
|
||||
/// </summary>
|
||||
/// <param name="bytesChange">字节数变化</param>
|
||||
/// <returns>是否合理</returns>
|
||||
public static bool IsValidBytesChange(long bytesChange)
|
||||
{
|
||||
return bytesChange >= 0 && bytesChange < MAX_REASONABLE_SPEED;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 计算方法
|
||||
|
||||
/// <summary>
|
||||
/// 计算网速
|
||||
/// </summary>
|
||||
/// <param name="currentBytes">当前字节数</param>
|
||||
/// <param name="previousBytes">之前字节数</param>
|
||||
/// <param name="timeInterval">时间间隔(秒)</param>
|
||||
/// <returns>网速(字节/秒)</returns>
|
||||
public static double CalculateSpeed(long currentBytes, long previousBytes, double timeInterval)
|
||||
{
|
||||
if (!IsValidTimeInterval(timeInterval))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var bytesChange = currentBytes - previousBytes;
|
||||
if (!IsValidBytesChange(bytesChange))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bytesChange / timeInterval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算子网掩码
|
||||
/// </summary>
|
||||
/// <param name="unicast">单播地址信息</param>
|
||||
/// <returns>子网掩码字符串</returns>
|
||||
public static string CalculateSubnetMask(UnicastIPAddressInformation unicast)
|
||||
{
|
||||
if (unicast == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 优先使用IPv4Mask
|
||||
if (unicast.IPv4Mask != null)
|
||||
{
|
||||
return unicast.IPv4Mask.ToString();
|
||||
}
|
||||
|
||||
// 通过前缀长度计算
|
||||
var prefixLength = unicast.PrefixLength;
|
||||
if (prefixLength > 0 && prefixLength <= IPV4_PREFIX_MAX)
|
||||
{
|
||||
var mask = (uint)(0xFFFFFFFF << (IPV4_PREFIX_MAX - prefixLength));
|
||||
return new IPAddress(new byte[]
|
||||
{
|
||||
(byte)(mask >> 24),
|
||||
(byte)(mask >> 16),
|
||||
(byte)(mask >> 8),
|
||||
(byte)mask
|
||||
}).ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Debug($"计算子网掩码时发生错误: {ex.Message}");
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 获取方法
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络接口类型描述
|
||||
/// </summary>
|
||||
/// <param name="type">接口类型</param>
|
||||
/// <returns>类型描述</returns>
|
||||
public static string GetInterfaceTypeDescription(NetworkInterfaceType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
NetworkInterfaceType.Ethernet => "以太网",
|
||||
NetworkInterfaceType.Wireless80211 => "Wi-Fi",
|
||||
NetworkInterfaceType.Loopback => "环回接口",
|
||||
NetworkInterfaceType.Ppp => "PPP",
|
||||
NetworkInterfaceType.Tunnel => "隧道",
|
||||
NetworkInterfaceType.Slip => "SLIP",
|
||||
NetworkInterfaceType.TokenRing => "令牌环",
|
||||
NetworkInterfaceType.Fddi => "FDDI",
|
||||
NetworkInterfaceType.BasicIsdn => "基本ISDN",
|
||||
NetworkInterfaceType.PrimaryIsdn => "主要ISDN",
|
||||
NetworkInterfaceType.GenericModem => "通用调制解调器",
|
||||
NetworkInterfaceType.FastEthernetT => "快速以太网T",
|
||||
NetworkInterfaceType.Isdn => "ISDN",
|
||||
NetworkInterfaceType.FastEthernetFx => "快速以太网FX",
|
||||
NetworkInterfaceType.Wman => "无线城域网",
|
||||
NetworkInterfaceType.Wwanpp => "无线广域网PP",
|
||||
NetworkInterfaceType.Wwanpp2 => "无线广域网PP2",
|
||||
_ => type.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取系统运行时间
|
||||
/// </summary>
|
||||
/// <returns>系统运行时间</returns>
|
||||
public static TimeSpan GetSystemUptime()
|
||||
{
|
||||
return TimeSpan.FromMilliseconds(Environment.TickCount64);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取外网IP地址
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>外网IP信息</returns>
|
||||
public static async Task<ExternalIPInfo> GetExternalIPAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var externalInfo = new ExternalIPInfo();
|
||||
|
||||
using var client = new HttpClient();
|
||||
client.Timeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
// 获取IPv4地址
|
||||
externalInfo.IPv4 = await GetIPv4AddressAsync(client, cancellationToken);
|
||||
|
||||
// 获取IPv6地址
|
||||
externalInfo.IPv6 = await GetIPv6AddressAsync(client, cancellationToken);
|
||||
|
||||
return externalInfo;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
||||
/// <summary>
|
||||
/// 获取IPv4外网地址
|
||||
/// </summary>
|
||||
private static async Task<string> GetIPv4AddressAsync(HttpClient client, CancellationToken cancellationToken)
|
||||
{
|
||||
var ipv4Services = new[]
|
||||
{
|
||||
"https://api.ipify.org",
|
||||
"https://icanhazip.com",
|
||||
"https://ipv4.icanhazip.com",
|
||||
"https://checkip.amazonaws.com"
|
||||
};
|
||||
|
||||
foreach (var service in ipv4Services)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await client.GetStringAsync(service, cancellationToken);
|
||||
var ip = response?.Trim();
|
||||
if (!string.IsNullOrEmpty(ip) && IPAddress.TryParse(ip, out _))
|
||||
{
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Debug($"获取IPv4地址失败 ({service}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return "无法获取";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取IPv6外网地址
|
||||
/// </summary>
|
||||
private static async Task<string> GetIPv6AddressAsync(HttpClient client, CancellationToken cancellationToken)
|
||||
{
|
||||
var ipv6Services = new[]
|
||||
{
|
||||
"https://api6.ipify.org",
|
||||
"https://ipv6.icanhazip.com"
|
||||
};
|
||||
|
||||
foreach (var service in ipv6Services)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await client.GetStringAsync(service, cancellationToken);
|
||||
var ip = response?.Trim();
|
||||
if (!string.IsNullOrEmpty(ip) && IPAddress.TryParse(ip, out _))
|
||||
{
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Debug($"获取IPv6地址失败 ({service}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return "不支持或无法获取";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 外网IP信息
|
||||
/// </summary>
|
||||
public class ExternalIPInfo
|
||||
{
|
||||
public string IPv4 { get; set; } = string.Empty;
|
||||
public string IPv6 { get; set; } = string.Empty;
|
||||
public string ISP { get; set; } = string.Empty;
|
||||
public string Location { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网速信息
|
||||
/// </summary>
|
||||
public class NetworkSpeed
|
||||
{
|
||||
public double DownloadSpeed { get; set; }
|
||||
public double UploadSpeed { get; set; }
|
||||
public double TotalSpeed => DownloadSpeed + UploadSpeed;
|
||||
|
||||
public string DownloadSpeedText => NetworkHelper.FormatSpeed(DownloadSpeed);
|
||||
public string UploadSpeedText => NetworkHelper.FormatSpeed(UploadSpeed);
|
||||
public string TotalSpeedText => NetworkHelper.FormatSpeed(TotalSpeed);
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using NetVizor.Common.Models;
|
||||
|
||||
namespace NetVizor.Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility methods to retrieve network interface information
|
||||
/// using the Windows IP Helper API (iphlpapi.dll).
|
||||
/// This class does not require administrator privileges.
|
||||
/// </summary>
|
||||
public static class NetworkInfoUtil
|
||||
{
|
||||
#region P/Invoke Definitions
|
||||
|
||||
private const int ERROR_SUCCESS = 0;
|
||||
private const int MAX_INTERFACE_NAME_LEN = 256;
|
||||
private const int MAXLEN_PHYSADDR = 8;
|
||||
private const int MAXLEN_IFDESCR = 256;
|
||||
|
||||
[DllImport("iphlpapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern uint GetIfTable2(out IntPtr pIfTable);
|
||||
|
||||
[DllImport("iphlpapi.dll", SetLastError = true)]
|
||||
private static extern void FreeMibTable(IntPtr pMibTable);
|
||||
|
||||
private enum TunnelType
|
||||
{
|
||||
TUNNEL_TYPE_NONE = 0,
|
||||
TUNNEL_TYPE_OTHER = 1,
|
||||
TUNNEL_TYPE_DIRECT = 2,
|
||||
TUNNEL_TYPE_6TO4 = 11,
|
||||
TUNNEL_TYPE_ISATAP = 13,
|
||||
TUNNEL_TYPE_TEREDO = 14,
|
||||
TUNNEL_TYPE_IPHTTPS = 15
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct MIB_IF_ROW2
|
||||
{
|
||||
public long InterfaceLuid;
|
||||
public uint InterfaceIndex;
|
||||
public Guid InterfaceGuid;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_INTERFACE_NAME_LEN + 1)]
|
||||
public string Alias;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXLEN_IFDESCR + 1)]
|
||||
public string Description;
|
||||
|
||||
public uint PhysicalAddressLength;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAXLEN_PHYSADDR)]
|
||||
public byte[] PhysicalAddress;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAXLEN_PHYSADDR)]
|
||||
public byte[] PermanentPhysicalAddress;
|
||||
|
||||
public uint Mtu;
|
||||
public NetworkInterfaceType Type;
|
||||
public TunnelType TunnelType;
|
||||
public uint MediaConnectState;
|
||||
public uint MediaDuplexState;
|
||||
public uint InterfaceAndOperStatusFlags;
|
||||
public OperationalStatus OperStatus;
|
||||
public uint AdminStatus;
|
||||
public uint NetLuidIndex;
|
||||
public uint NdisMedium;
|
||||
public uint NdisPhysicalMedium;
|
||||
|
||||
public uint HardareInterface;
|
||||
|
||||
// --- Start of Corrected Fields ---
|
||||
public ulong ReceiveLinkSpeed;
|
||||
|
||||
public ulong TransmitLinkSpeed;
|
||||
|
||||
// --- End of Corrected Fields ---
|
||||
public ulong OutOctets;
|
||||
public ulong InOctets;
|
||||
public ulong OutUcastPkts;
|
||||
public ulong InUcastPkts;
|
||||
public ulong OutNUcastPkts;
|
||||
public ulong InNUcastPkts;
|
||||
public ulong OutDiscards;
|
||||
public ulong InDiscards;
|
||||
public ulong OutErrors;
|
||||
public ulong InErrors;
|
||||
public ulong OutUcastOctets;
|
||||
public ulong InUcastOctets;
|
||||
public ulong OutMulticastOctets;
|
||||
public ulong InMulticastOctets;
|
||||
public ulong OutBroadcastOctets;
|
||||
public ulong InBroadcastOctets;
|
||||
public uint OutQLen;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct MIB_IF_TABLE2
|
||||
{
|
||||
public uint NumEntries;
|
||||
// The Table field is a placeholder here. We'll calculate its position manually.
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all network interfaces on the system.
|
||||
/// </summary>
|
||||
/// <returns>A list of NetworkInterfaceInfo objects.</returns>
|
||||
/// <exception cref="Win32Exception">Thrown if the underlying Windows API call fails.</exception>
|
||||
public static List<NetworkInterfaceInfo> GetAllInterfaces()
|
||||
{
|
||||
IntPtr pIfTable = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
uint result = GetIfTable2(out pIfTable);
|
||||
if (result != ERROR_SUCCESS)
|
||||
{
|
||||
throw new Win32Exception((int)result);
|
||||
}
|
||||
|
||||
var ifTable = (MIB_IF_TABLE2)Marshal.PtrToStructure(pIfTable, typeof(MIB_IF_TABLE2));
|
||||
var interfaces = new List<NetworkInterfaceInfo>((int)ifTable.NumEntries);
|
||||
|
||||
IntPtr currentPtr = new IntPtr(pIfTable.ToInt64() + Marshal.SizeOf(typeof(uint)));
|
||||
|
||||
for (int i = 0; i < ifTable.NumEntries; i++)
|
||||
{
|
||||
var row = (MIB_IF_ROW2)Marshal.PtrToStructure(currentPtr, typeof(MIB_IF_ROW2));
|
||||
interfaces.Add(MapRowToInterfaceInfo(row));
|
||||
currentPtr = new IntPtr(currentPtr.ToInt64() + Marshal.SizeOf(typeof(MIB_IF_ROW2)));
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pIfTable != IntPtr.Zero)
|
||||
{
|
||||
FreeMibTable(pIfTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets detailed information for a specific network interface by its index.
|
||||
/// </summary>
|
||||
/// <param name="interfaceIndex">The index of the interface to retrieve.</param>
|
||||
/// <returns>A NetworkInterfaceInfo object, or null if not found.</returns>
|
||||
public static NetworkInterfaceInfo? GetInterfaceByIndex(uint interfaceIndex)
|
||||
{
|
||||
var allInterfaces = GetAllInterfaces();
|
||||
foreach (var iface in allInterfaces)
|
||||
{
|
||||
if (iface.Index == interfaceIndex)
|
||||
{
|
||||
return iface;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static NetworkInterfaceInfo MapRowToInterfaceInfo(MIB_IF_ROW2 row)
|
||||
{
|
||||
// Guard against invalid address length reported by the API
|
||||
int macAddrLen = (int)Math.Min(row.PhysicalAddressLength, (uint)MAXLEN_PHYSADDR);
|
||||
var macBytes = new byte[macAddrLen];
|
||||
if (macAddrLen > 0)
|
||||
{
|
||||
Array.Copy(row.PhysicalAddress, macBytes, macAddrLen);
|
||||
}
|
||||
|
||||
return new NetworkInterfaceInfo
|
||||
{
|
||||
Index = row.InterfaceIndex,
|
||||
InterfaceId = row.InterfaceGuid.ToString(),
|
||||
Alias = row.Alias,
|
||||
Description = row.Description,
|
||||
Type = row.Type,
|
||||
Status = row.OperStatus,
|
||||
Speed = row.ReceiveLinkSpeed, // Correctly use the speed field from the struct
|
||||
MacAddress = macAddrLen > 0 ? BitConverter.ToString(macBytes).Replace('-', ':') : string.Empty,
|
||||
BytesSent = (long)row.OutOctets,
|
||||
BytesReceived = (long)row.InOctets
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Net.NetworkInformation;
|
||||
using Common.Logger;
|
||||
|
||||
namespace Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络监控帮助类 - 使用 .NET 内置的 NetworkInterface 类
|
||||
/// </summary>
|
||||
public class NetworkMonitorHelper1
|
||||
{
|
||||
#region 公共类和方法
|
||||
|
||||
/// <summary>
|
||||
/// 网络接口信息
|
||||
/// </summary>
|
||||
public class NetworkInterface
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Id { get; set; }
|
||||
public uint Index { get; set; }
|
||||
public long BytesReceived { get; set; }
|
||||
public long BytesSent { get; set; }
|
||||
public bool IsConnected { get; set; }
|
||||
public NetworkInterfaceType Type { get; set; }
|
||||
public string TypeDescription { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网速信息
|
||||
/// </summary>
|
||||
public class NetworkSpeed
|
||||
{
|
||||
public double DownloadSpeed { get; set; } // bytes per second
|
||||
public double UploadSpeed { get; set; } // bytes per second
|
||||
public double TotalSpeed => DownloadSpeed + UploadSpeed;
|
||||
|
||||
// 格式化显示
|
||||
public string DownloadSpeedText => FormatSpeed(DownloadSpeed);
|
||||
public string UploadSpeedText => FormatSpeed(UploadSpeed);
|
||||
public string TotalSpeedText => FormatSpeed(TotalSpeed);
|
||||
|
||||
private string FormatSpeed(double bytesPerSecond)
|
||||
{
|
||||
// 防止显示负数或异常大的数值
|
||||
if (bytesPerSecond < 0 || double.IsNaN(bytesPerSecond) || double.IsInfinity(bytesPerSecond))
|
||||
{
|
||||
return "0.00 B/s";
|
||||
}
|
||||
|
||||
string[] sizes = { "B/s", "KB/s", "MB/s", "GB/s" };
|
||||
int order = 0;
|
||||
while (bytesPerSecond >= 1024 && order < sizes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
bytesPerSecond = bytesPerSecond / 1024;
|
||||
}
|
||||
|
||||
return $"{bytesPerSecond:0.00} {sizes[order]}";
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存上次的统计数据
|
||||
private static Dictionary<string, (long bytesReceived, long bytesSent, DateTime updateTime)> previousStats =
|
||||
new Dictionary<string, (long, long, DateTime)>();
|
||||
|
||||
private static bool isFirstRun = true;
|
||||
|
||||
/// <summary>
|
||||
/// 获取接口类型描述
|
||||
/// </summary>
|
||||
private static string GetInterfaceTypeDescription(NetworkInterfaceType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case NetworkInterfaceType.Ethernet:
|
||||
return "以太网";
|
||||
case NetworkInterfaceType.Wireless80211:
|
||||
return "Wi-Fi";
|
||||
case NetworkInterfaceType.Loopback:
|
||||
return "环回接口";
|
||||
case NetworkInterfaceType.Ppp:
|
||||
return "PPP";
|
||||
case NetworkInterfaceType.Tunnel:
|
||||
return "隧道";
|
||||
default:
|
||||
return type.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有网络接口
|
||||
/// </summary>
|
||||
public static List<NetworkInterface> GetNetworkInterfaces()
|
||||
{
|
||||
var interfaces = new List<NetworkInterface>();
|
||||
|
||||
try
|
||||
{
|
||||
var networkInterfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
foreach (var ni in networkInterfaces)
|
||||
{
|
||||
// 跳过环回接口
|
||||
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||
{
|
||||
Log.Info($"跳过环回接口: {ni.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取IPv4统计信息
|
||||
IPv4InterfaceStatistics stats = null;
|
||||
try
|
||||
{
|
||||
stats = ni.GetIPv4Statistics();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error4Ctx($"无法获取接口 {ni.Name} 的统计信息: {ex.Message}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var networkInterface = new NetworkInterface
|
||||
{
|
||||
Name = ni.Name,
|
||||
Description = ni.Description,
|
||||
Id = ni.Id,
|
||||
Index = 0, // NetworkInterface 类不提供 Index,使用 Id 作为唯一标识
|
||||
BytesReceived = stats.BytesReceived,
|
||||
BytesSent = stats.BytesSent,
|
||||
IsConnected = ni.OperationalStatus == OperationalStatus.Up,
|
||||
Type = ni.NetworkInterfaceType,
|
||||
TypeDescription = GetInterfaceTypeDescription(ni.NetworkInterfaceType)
|
||||
};
|
||||
|
||||
interfaces.Add(networkInterface);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error4Ctx($"获取网络接口时发生错误: {ex.Message}");
|
||||
Log.Error4Ctx($"错误堆栈: {ex.StackTrace}");
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算指定网络接口的网速
|
||||
/// </summary>
|
||||
public static NetworkSpeed CalculateSpeed(uint interfaceIndex)
|
||||
{
|
||||
// 由于使用了 NetworkInterface 类,这里改为使用接口名称查找
|
||||
var interfaces = GetNetworkInterfaces();
|
||||
return CalculateSpeedByName(interfaces, interfaceIndex.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据接口名称计算网速
|
||||
/// </summary>
|
||||
public static NetworkSpeed CalculateSpeedByName(string interfaceName)
|
||||
{
|
||||
var interfaces = GetNetworkInterfaces();
|
||||
return CalculateSpeedByName(interfaces, interfaceName);
|
||||
}
|
||||
|
||||
private static NetworkSpeed CalculateSpeedByName(List<NetworkInterface> currentInterfaces, string interfaceName)
|
||||
{
|
||||
Log.Info($"计算接口 {interfaceName} 的网速...");
|
||||
|
||||
var currentInterface =
|
||||
currentInterfaces.FirstOrDefault(i => i.Name == interfaceName || i.Id == interfaceName);
|
||||
|
||||
if (currentInterface == null)
|
||||
{
|
||||
Log.Warning($"未找到接口 {interfaceName}");
|
||||
return new NetworkSpeed();
|
||||
}
|
||||
|
||||
var now = DateTime.Now;
|
||||
var speed = new NetworkSpeed();
|
||||
string key = currentInterface.Id;
|
||||
|
||||
// 如果是第一次运行或没有该接口的历史数据,只保存数据
|
||||
if (isFirstRun || !previousStats.ContainsKey(key))
|
||||
{
|
||||
previousStats[key] = (currentInterface.BytesReceived, currentInterface.BytesSent, now);
|
||||
Log.Info($"接口 {interfaceName} 第一次记录数据");
|
||||
if (isFirstRun) isFirstRun = false;
|
||||
return speed;
|
||||
}
|
||||
|
||||
var (prevBytesReceived, prevBytesSent, lastUpdateTime) = previousStats[key];
|
||||
var timeDiff = (now - lastUpdateTime).TotalSeconds;
|
||||
|
||||
Log.Debug($"时间差: {timeDiff}秒");
|
||||
|
||||
if (timeDiff > 0.5) // 至少0.5秒的间隔
|
||||
{
|
||||
// 计算字节差
|
||||
long downloadBytes = currentInterface.BytesReceived - prevBytesReceived;
|
||||
long uploadBytes = currentInterface.BytesSent - prevBytesSent;
|
||||
|
||||
Log.Info($"下载字节差: {downloadBytes}, 上传字节差: {uploadBytes}");
|
||||
|
||||
// 防止计数器重置或异常值
|
||||
if (downloadBytes >= 0 && downloadBytes < 1000000000) // 小于1GB/s
|
||||
{
|
||||
speed.DownloadSpeed = downloadBytes / timeDiff;
|
||||
}
|
||||
|
||||
if (uploadBytes >= 0 && uploadBytes < 1000000000) // 小于1GB/s
|
||||
{
|
||||
speed.UploadSpeed = uploadBytes / timeDiff;
|
||||
}
|
||||
|
||||
Log.Info($"接口 {interfaceName} - 下载: {speed.DownloadSpeedText}, 上传: {speed.UploadSpeedText}");
|
||||
|
||||
// 更新缓存
|
||||
previousStats[key] = (currentInterface.BytesReceived, currentInterface.BytesSent, now);
|
||||
}
|
||||
|
||||
return speed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算所有网络接口的总网速
|
||||
/// </summary>
|
||||
public static NetworkSpeed CalculateTotalSpeed()
|
||||
{
|
||||
var currentInterfaces = GetNetworkInterfaces();
|
||||
var now = DateTime.Now;
|
||||
var totalSpeed = new NetworkSpeed();
|
||||
|
||||
// 如果是第一次运行,只保存数据
|
||||
if (isFirstRun)
|
||||
{
|
||||
foreach (var currentInterface in currentInterfaces)
|
||||
{
|
||||
previousStats[currentInterface.Id] =
|
||||
(currentInterface.BytesReceived, currentInterface.BytesSent, now);
|
||||
}
|
||||
|
||||
isFirstRun = false;
|
||||
Log.Info("第一次运行,初始化数据");
|
||||
return totalSpeed;
|
||||
}
|
||||
|
||||
foreach (var currentInterface in currentInterfaces)
|
||||
{
|
||||
string key = currentInterface.Id;
|
||||
|
||||
if (previousStats.ContainsKey(key))
|
||||
{
|
||||
var (prevBytesReceived, prevBytesSent, lastUpdateTime) = previousStats[key];
|
||||
var timeDiff = (now - lastUpdateTime).TotalSeconds;
|
||||
|
||||
if (timeDiff > 0.5) // 至少0.5秒的间隔
|
||||
{
|
||||
long downloadBytes = currentInterface.BytesReceived - prevBytesReceived;
|
||||
long uploadBytes = currentInterface.BytesSent - prevBytesSent;
|
||||
|
||||
// 防止计数器重置或异常值
|
||||
if (downloadBytes >= 0 && downloadBytes < 1000000000) // 小于1GB/s
|
||||
{
|
||||
totalSpeed.DownloadSpeed += downloadBytes / timeDiff;
|
||||
}
|
||||
|
||||
if (uploadBytes >= 0 && uploadBytes < 1000000000) // 小于1GB/s
|
||||
{
|
||||
totalSpeed.UploadSpeed += uploadBytes / timeDiff;
|
||||
}
|
||||
|
||||
// 更新缓存
|
||||
previousStats[key] = (currentInterface.BytesReceived, currentInterface.BytesSent, now);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 新接口,添加到缓存
|
||||
previousStats[key] = (currentInterface.BytesReceived, currentInterface.BytesSent, now);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Info($"总网速 - 下载: {totalSpeed.DownloadSpeedText}, 上传: {totalSpeed.UploadSpeedText}");
|
||||
|
||||
return totalSpeed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置统计数据
|
||||
/// </summary>
|
||||
public static void ResetStatistics()
|
||||
{
|
||||
Log.Info("重置网络统计数据");
|
||||
previousStats.Clear();
|
||||
isFirstRun = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using NetVizor.Common.Models;
|
||||
|
||||
namespace NetVizor.Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Monitors the real-time network speed (upload/download) for a specific network interface.
|
||||
/// </summary>
|
||||
public class NetworkSpeedMonitor
|
||||
{
|
||||
private readonly uint _interfaceIndex;
|
||||
private long _lastBytesSent;
|
||||
private long _lastBytesReceived;
|
||||
private DateTime _lastUpdateTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the real-time download speed in bytes per second.
|
||||
/// </summary>
|
||||
public double DownloadSpeedBps { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the real-time upload speed in bytes per second.
|
||||
/// </summary>
|
||||
public double UploadSpeedBps { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the NetworkSpeedMonitor for a given interface.
|
||||
/// </summary>
|
||||
/// <param name="interfaceInfo">The network interface to monitor.</param>
|
||||
public NetworkSpeedMonitor(NetworkInterfaceInfo interfaceInfo)
|
||||
{
|
||||
_interfaceIndex = interfaceInfo.Index;
|
||||
_lastBytesSent = interfaceInfo.BytesSent;
|
||||
_lastBytesReceived = interfaceInfo.BytesReceived;
|
||||
_lastUpdateTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the speed calculation. This method should be called periodically.
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
var currentInterfaceInfo = NetworkInfoUtil.GetInterfaceByIndex(_interfaceIndex);
|
||||
if (currentInterfaceInfo == null)
|
||||
{
|
||||
DownloadSpeedBps = 0;
|
||||
UploadSpeedBps = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTime = DateTime.UtcNow;
|
||||
var timeSpan = (currentTime - _lastUpdateTime).TotalSeconds;
|
||||
|
||||
if (timeSpan <= 0)
|
||||
{
|
||||
return; // Avoid division by zero if called too quickly
|
||||
}
|
||||
|
||||
long bytesSent = currentInterfaceInfo.BytesSent;
|
||||
long bytesReceived = currentInterfaceInfo.BytesReceived;
|
||||
|
||||
UploadSpeedBps = (bytesSent - _lastBytesSent) / timeSpan;
|
||||
DownloadSpeedBps = (bytesReceived - _lastBytesReceived) / timeSpan;
|
||||
|
||||
_lastBytesSent = bytesSent;
|
||||
_lastBytesReceived = bytesReceived;
|
||||
_lastUpdateTime = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user