C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C# ScottPlot网络流量监控

基于C#和ScottPlot开发专业级网络流量监控工具

作者:小码编匠

这篇文章主要为大家详细介绍了如何使用 C# 和强大的 ScottPlot 可视化库,从零开始构建一个专业级的网络流量监控工具,感兴趣的小伙伴可以跟随小编一起学习一下

前言

软件开发和系统运维中,网络性能是决定应用稳定性和用户体验的关键因素。

大家是否曾遇到过这样的情况:正在处理重要任务时,网络突然变得异常缓慢,却无法确定是哪个程序在"偷跑"流量?或者作为运维人员,需要实时掌握服务器的网络负载情况,但市面上的工具要么功能臃肿,要么界面陈旧难用。

本文将带你使用 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;
    }
}

注意事项

图表初始化与配置

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 - lastBytesSent) / timeSpan / 1024;
            
            // 🛡️ 防御性编程:确保速度不为负数
            downloadSpeed = Math.Max(0, downloadSpeed);
            uploadSpeed = 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;
    }
}

算法亮点

动态图表更新:

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 OnFormClosing(FormClosingEventArgs e)
{ 
    // 🧹 优雅关闭:清理资源
    updateTimer?.Stop();
    updateTimer?.Dispose();
    base.OnFormClosing(e);
}

实战应用场景

企业级应用

开发调试

技术要点

性能优化秘籍

1、异步 UI 更新:使用 Invoke 确保图表更新在主线程执行,保证线程安全。

2、数据点控制:限制历史数据数量,避免内存泄漏。

3、异常隔离:将图表更新等非核心功能的异常进行捕获,不影响数据采集主流程。

常见坑点规避

项目源码

Gitee:gitee.com/smallcore/DotNetCore/tree/master/DotNetCore/NetworkTool

总结

通过这个项目,我们不仅成功开发了一个功能完备、界面美观的网络流量监控工具,更重要的是深入掌握了多项核心技术:从 NetworkInterface 类的高级用法,到 ScottPlot 的数据可视化最佳实践,再到性能优化与异常处理的工程思维。这个工具可以直接应用于实际工作场景,不管是系统运维还是应用开发,都能提供有力的支持。

更重要的是,它展示了如何将简单的技术组件组合起来,解决复杂的实际问题,这正是编程的魅力所在。

到此这篇关于基于C#和ScottPlot开发专业级网络流量监控工具的文章就介绍到这了,更多相关C# ScottPlot网络流量监控内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文