目录
- 前言
- 项目功能
- 核心技术
- 核心架构
- 核心功能实现
- 注意事项
- 设计技巧
- 算法亮点
- 高级优化技巧
- 实战应用场景
- 企业级应用
- 开发调试
- 技术要点
- 性能优化秘籍
- 常见坑点规避
- 总结
前言
软件开发和系统运维中,网络性能是决定应用稳定性和用户体验的关键因素。
大家是否曾遇到过这样的情况:正在处理重要任务时,网络突然变得异常缓慢,却无法确定是哪个程序在"偷跑"流量?或者作为运维人员,需要实时掌握服务器的网络负载情况,但市面上的工具要么功能臃肿,要么界面陈旧难用。
本文将带你使用 C# 和强大的 ScottPlot 可视化库,从零开始构建一个专业级的网络流量监控工具。它不仅具备实时监控、动态图表、多网卡支持等核心功能,还拥有美观的界面和高度可定制性。更重要的是,整个过程将帮助大家深入理解网络编程、数据可视化与性能优化的核心技术。
项目功能
这款网络流量监控工具在解决实际问题,具备以下核心功能:
- 实时监控上传与下载速度,精确到每秒。
- 通过动态折线图展示历史流量数据,直观呈现网络波动。
- 支持选择多个网络接口,方便在多网卡环境下进行监控。
- 展示详细的网络统计信息,如总收发字节数。
- 提供启动/停止监控的控制按钮,操作灵活。
核心技术
项目基于 .NET Framework 和 WinForm 开发,结合 ScottPlot 这一强大的开源绘图库,实现高效的数据可视化。
必备 NuGet 包:
<PackageReference Include="ScottPlot.WinForms" Version="5.0.0" />
关键命名空间:
using ScottPlot; // 图表核心功能 using ScottPlot.WinForms; // WinForms集成 using System.Net.NetworkInformation; // 网络接口操作 using Timer = System.Windows.Forms.Timer; // 定时器
核心架构
首先定义工具所需的数据结构和变量:
public partial class Form1 : Form
{
// 定时器和网络接口
private Timer updateTimer;
private NetworkInterface selectedInterface;
private List<NetworkInterface> networkInterfaces;
// 历史数据存储
private List<double> downloadHistory;
private List<double> uploadHistory;
private List<DateTime> timeHistory;
// 基准数据用于计算差值
private long lastBytesReceived;
private long lastBytesSent;
private DateTime lastUpdateTime;
// 数据点控制
private int maxHistoryPoints = 60; // 保留60个数据点(1分钟历史)
// ScottPlot 图表对象
private ScottPlot.Plottables.Scatter downloadPlot;
private ScottPlot.Plottables.Scatter uploadPlot;
}
设计亮点
采用"差值计算法"来精确测量网络速度。通过记录上一次的接收和发送字节数,结合时间间隔,计算出实时速率,避免了累积误差。
核心功能实现
网络接口发现与初始化:
private void LoadNetworkInterfaces()
{
// 获取所有活跃的非回环网络接口
networkInterfaces = NetworkInterface.GetAllNetworkInterfaces()
.Where(ni => ni.OperationalStatus == OperationalStatus.Up &&
ni.NetworkInterfaceType != NetworkInterfaceType.Loopback)
.ToList();
comboBoxInterfaces.Items.Clear();
foreach (var ni in networkInterfaces)
{
// 显示友好的接口名称
comboBoxInterfaces.Items.Add($"{ni.Name} ({ni.NetworkInterfaceType})");
}
if (comboBoxInterfaces.Items.Count > 0)
{
comboBoxInterfaces.SelectedIndex = 0;
selectedInterface = networkInterfaces[0];
// 关键:初始化基准值,确保第一次计算的准确性
var stats = selectedInterface.GetIPv4Statistics();
lastBytesReceived = stats.BytesReceived;
lastBytesSent = stats.BytesSent;
}
}
注意事项
- 过滤掉回环接口(Loopback)以避免干扰。
- 仅选择处于“运行中”(Up)状态的接口。
- 初始化基准值是确保首次计算准确的关键步骤。
图表初始化与配置
private void SetupChart()
{
// 清除所有绘图对象
formsPlot.Plot.Clear();
formsPlot.Plot.Legend.FontName = "SimSun"; // 中文字体支持
// 坐标轴美化
formsPlot.Plot.Axes.Left.Label.Text = "速度 (KB/s)";
formsPlot.Plot.Axes.Bottom.Label.Text = "时间";
formsPlot.Plot.Axes.Left.Label.FontSize = 12;
formsPlot.Plot.Axes.Bottom.Label.FontSize = 12;
formsPlot.Plot.Axes.Bottom.Label.FontName = "SimSun";
formsPlot.Plot.Axes.Left.Label.FontName = "SimSun";
// 创建下载速度线条
downloadPlot = formsPlot.Plot.Add.Scatter(new double[0], new double[0]);
downloadPlot.Color = ScottPlot.Color.Fromhtml("#007BFF"); // 专业蓝色
downloadPlot.LineWidth = 2;
downloadPlot.MarkerSize = 0; // 不显示数据点,保持线条流畅
downloadPlot.LegendText = "下载速度";
// 创建上传速度线条
uploadPlot = formsPlot.Plot.Add.Scatter(new double[0], new double[0]);
uploadPlot.Color = ScottPlot.Color.FromHtml("#DC3545"); // 警示红色
uploadPlot.LineWidth = 2;
uploadPlot.MarkerSize = 0;
uploadPlot.LegendText = "上传速度";
// 显示图例并设置时间轴
formsPlot.Plot.ShowLegend(Alignment.UpperRight);
formsPlot.Plot.Axes.DateTimeTicksBottom(); // 时间轴格式化
formsPlot.Refresh();
}
设计技巧
- 使用专业配色方案提升视觉效果。
- 时间轴自动格式化,提升用户体验。
- 图例置于右上角,避免遮挡主要数据。
实时数据更新核心逻辑:
private void UpdateTimer_Tick(object sender, EventArgs e)
{
if (selectedInterface == null) return;
try
{
var stats = selectedInterface.GetIPv4Statistics();
var currentTime = DateTime.Now;
var timeSpan = (currentTime - lastUpdateTime).TotalSeconds;
// 核心算法:差值法计算实时速度
if (timeSpan > 0 && lastBytesReceived > 0 && lastBytesSent > 0)
{
// 计算速度 (KB/s)
double downloadSpeed = (stats.BytesReceived - lastBytesReceived) / timeSpan / 1024;
double uploadSpeed = (stats.BytesSent - lastByphptesSent) / timeSpan / 1024;
// ️ 防御性编程:确保速度不为负数
downloadSpeed = Math.Max(0, downloadSpeed);
uploadSpeephpd = Math.Max(0, uploadSpeed);
// 更新各个显示组件
UpdateRealTimeDisplay(downloadSpeed, uploadSpeed, stats);
UpdateHistory(downloadSpeed, uploadSpeed, currentTime);
UpdateChart();
}
// 更新基准值
lastBytesReceived = stats.BytesReceived;
lastBytesSent = stats.BytesSent;
lastUpdateTime = currentTime;
}
catch (Exception ex)
{
// 优雅的错误处理
labelStatus.Text = $"监控错误: {ex.Message}";
labelStatus.ForeColor = System.Drawing.Color.Red;
}
}
算法亮点
android- 通过差值除以时间间隔得到精确的实时速度。
- 防御性编程确保速度值非负。
- 异常处理机制保证程序整体稳定性。
动态图表更新:
private void UpdateChart()
{
if (timeHistory.Count == 0) return;
try
{
// 时间格式转换:DateTime转OADate用于ScottPlot
double[] timeArray = timeHistory.Select(t => t.ToOADate()).ToArray();
double[] downloadArray = downloadHistory.ToArray();
double[] uploadArray = uploadHistory.ToArray();
// 动态更新策略:移除旧对象,添加新对象
if (downloadPlot != null)
formsPlot.Plot.Remove(downloadPlot);
编程 if (uploadPlot != null)
formsPlot.Plot.Remove(uploadPlot);
// 重新创建图表对象
downloadPlot = formsPlot.Plot.Add.Scatter(timeArray, downloadArray);
downloadPlot.Color = ScottPlot.Color.FromHtml("#007BFF");
downloadPlot.LineWidth = 2;
downloadPlot.MarkerSize = 0;
downloadPlot.LegendText = "下载速度";
uploadPlot = formsPlot.Plot.Add.Scatter(timeArray, uploadArray);
uploadPlot.Color = ScottPlot.Color.FromHtml("#DC3545");
uploadPlot.LineWidth = 2;
uploadPlot.MarkerSize = 0;
uploadPlot.LegendText = "上传速度";
formsPlot.Plot.ShowLegend(Alignment.UpperRight);
// 智能坐标轴调整
formsPlot.Plot.Axes.AutoScale();
// 设置X轴显示最近的时间窗口
if (timeArray.Length > 0)
{
var latestTime = timeArray[timeArray.Length - 1];
var earliestTime = DateTime.Now.AddSeconds(-maxHistoryPoints).ToOADate();
formsPlot.Plot.Axes.SetLimitsX(earliestTime, latestTime);
}
formsPlot.Refresh();
}
catch (Exception ex)
{
// ️ 图表更新失败不影响主程序
System.Diagnostics.Debug.WriteLine($"Chart update error: {ex.Message}");
}
}
用户界面交互:
private void comboBoxInterfaces_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBoxInterfaces.SelectedIndex >= 0)
{
selectedInterface = networkInterfaces[comboBoxInterfaces.SelectedIndex];
// 重置统计基准
var stats = selectedInterface.GetIPv4Statistics();
lastBytesReceived = stats.BytesReceived;
lastBytesSent = stats.BytesSent;
lastUpdateTime = DateTime.Now;
// 清空历史数据重新开始
downloadHistory.Clear();
uploadHistory.Clear();
timeHistory.Clear();
// 重新初始化图表
formsPlot.Plot.Clear();
SetupChart();
labelStatus.Text = $"已切换到: {selectedInterface.Name}";
labelStatus.ForeColor = System.Drawing.Color.Blue;
}
}
private void buttonStartStop_Click(object sender, EventArgs e)
{
if (updateTimer.Enabled)
{
// 停止监控
updateTimer.Stop();
buttonStartStop.Text = "开始监控";
buttonStartStop.BackColor = System.Drawing.Color.FromArgb(40, 167, 69);
labelStatus.Text = "监控已停止";
labelStatus.ForeColor = System.Drawing.Color.Red;
}
else
{
// ▶️ 开始监控
updateTimer.Start();
buttonStartStop.Text = "停止监控";
buttonStartStop.BackColor = System.Drawing.Color.FromArgb(220, 53, 69);
labelStatus.Text = "监控已开始";
labelStatus.ForeColor = System.Drawing.Color.Green;
}
}
高级优化技巧
数据格式化工具:
private string FormatSpeed(double bytesPerSecond)
{
// 智能单位转换
if (bytesPerSecond < 1024)
return $"{bytesPerSecond:F1} B/s";
else if (bytesPerSecond < 1024 * 1024)
return $"{bytesPerSecond / 1024:F1} KB/s";
else if (bytesPerSecond < 1024L * 1024 * 1024)
return $"{bytesPerSecond / (1024 * 1024):F1} MB/s";
else
return $"{bytesPerSecond / (1024L * 1024 * 1024):F1} GB/s";
}
private string FormatBytes(long bytes)
{
// 流量统计格式化
if (bytes < 1024)
return $"{bytes} B";
else if (bytes < 1024 * 1024)
return $"{bytes / 1024.0:F1} KB";
else if (bytes < 1024L * 1024 * 1024)
return $"{bytes / (1024.0 * 1024):F1} MB";
else if (bytes < 1024L * 1024 * 1024 * 1024)
return $"{bytes / (1024.0 * 1024 * 1024):F1} GB";
else
return $"{bytes / (1024.0 * 1024 * 1024 * 1024):F1} TB";
}
内存管理与资源清理
private void UpdateHistory(double downloadSpeed, double uploadSpeed, DateTime currentTime)
{
downloadHistory.Add(downloadSpeed);
uploadHistory.Add(uploadSpeed);
timeHistory.Add(currentTime);
// 滑动窗口:自动清理过期数据
while (downloadHistory.Count > maxHistoryPoints)
{
downloadHistory.RemoveAt(0);
uploadHistory.RemoveAt(0);
timeHistory.RemoveAt(0);
}
}
protected override void OnFormClos编程ing(FormClosingEventArgs e)
{
// 优雅关闭:清理资源
updateTimer?.Stop();
updateTimer?.Dispose();
base.OnFormClosing(e);
}
实战应用场景
企业级应用
- 服务器监控:部署在生产服务器上,实时监控带宽使用情况。
- 网络诊断:快速定位网络性能瓶颈,排查异常流量。
- 流量统计:为网络容量规划和成本控制提供数据支持。
开发调试
- API 测试:监控接口调用过程中的网络开销,评估性能。
- 性能优化:识别应用中网络密集型的操作,进行针对性优化。
- 资源监控:实时追踪特定应用或进程的网络资源消耗。
技术要点
性能优化秘籍
1、异步 UI 更新:使用 Invoke 确保图表更新在主线程执行,保证线程安全。
2、数据点控制:限制历史数据数量,避免内存泄漏。
3、异常隔离:将图表更新等非核心功能的异常进行捕获,不影响数据采集主流程。
常见坑点规避
- 初始化陷阱:必须在开始监控前设置正确的基准字节数。
- 负数速度:网络接口重置可能导致字节数归零,需使用 Math.Max 防止速度为负。
- 线程安全:所有 UI 操作必须在主线程中执行。
项目源码
Gitee:gitee.com/smallcore/DotNetCore/tree/master/DotNetCore/NetworkTool
总结
通过这个项目,我们不仅成功开发了一个功能完备、界面美观的网络流量监控工具,更重要的是深入掌握了多项核心技术:从 NetworkInterface 类的高级用法,到 ScottPlot 的数据可视化最佳实践,再到性能优化与异常处理的工程思维。这个工具可以直接应用于实际工作场景,不管是系统运维还是应用开发,都能提供有力的支持。
更重要的是,它展示了如何将简单的技术组件组合起来,解决复杂的实际问题,这正是编程的魅力所在。
到此这篇关于基于C#和ScottPlot开发专业级网络流量监控工具的文章就介绍到这了,更多相关C# ScottPlot网络流量监控内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论