一、项目背景详细介绍

在现代计算机系统中,网络带宽占用率(Network Utilization) 是衡量系统性能的重要指标之一。
许多系统监控软件(如任务管理器、网络流量监控器、资源管理器)都实时显示网络使用率。

在企业级应用、服务器管理、性能分析、网络优化、或智能控制系统中,
我们常常需要获取当前网卡实时上传与下载速率、带宽利用率等数据。

本项目的目标是使用 VC++(Win32 API + IP Helper API) 实现:

  • 自动枚举系统中所有网络接口;

  • 实时读取网络传输字节数;

  • 计算带宽利用率(%);

  • 定时刷新网络占用情况。

这种方法的优点:

  • 无需安装第三方库;

  • 兼容 Windows XP 至 Windows 11;

  • 可轻松扩展为网络监控工具。


二、项目需求详细介绍

2.1 功能性需求

  1. 获取系统中所有网卡接口名称;

  2. 获取每个网卡的传输速率(bytes sent / bytes received);

  3. 每隔一定时间刷新网络数据;

  4. 计算网卡带宽占用率(发送、接收、总占用率);

  5. 将结果输出到控制台。

2.2 非功能性需求

  • 仅使用 Windows API;

  • 无需管理员权限;

  • 程序稳定、安全;

  • 支持多网卡;

  • 支持 Unicode。


三、相关技术详细介绍

3.1 IP Helper API 概述

IP Helper API(iphlpapi.dll)是微软提供的网络接口信息访问库,
头文件为 <iphlpapi.h>,链接库为 iphlpapi.lib。

常用函数如下:

函数功能
GetIfTable获取网卡接口表(旧版接口)
GetIfEntry获取单个网卡接口信息
GetIfTable2 / GetIfEntry2新版接口(Vista+)
GetAdaptersAddresses获取网卡地址及信息
GetAdaptersInfo旧版接口

本项目选用 GetIfTable 读取网络接口信息。
结构体定义如下:


typedef struct _MIB_IFROW {
  WCHAR wszName[MAX_INTERFACE_NAME_LEN];
  DWORD dwIndex;
  DWORD dwType;
  DWORD dwMtu;
  DWORD dwSpeed;       // 接口速率(比特/秒)
  DWORD dwPhysAddrLen;
  BYTE  bPhysAddr[MAXLEN_PHYSADDR];
  DWORD dwAdminStatus;
  DWORD dwOperStatus;
  DWORD dwLastChange;
  DWORD dwInOctets;    // 接收字节数
  DWORD dwInUcastPkts;
  DWORD dwInNUcastPkts;
  DWORD dwInDiscards;
  DWORD dwInErrors;
  DWORD dwOutOctets;   // 发送字节数
  DWORD dwOutUcastPkts;
  DWORD dwOutNUcastPkts;
  DWORD dwOutDiscards;
  DWORD dwOutErrors;
  DWORD dwOutQLen;
  DWORD dwDescrLen;
  BYTE  bDescr[MAXLEN_IFDESCR];
} MIB_IFROW;


3.2 网络占用率计算原理

假设:

  • 接口带宽:dwSpeed(单位:bit/s)

  • 两次采样时间间隔:Δt

  • 接收字节差值:ΔInOctets

  • 发送字节差值:ΔOutOctets

则网络总流量为:


ΔBytes = ΔInOctets + ΔOutOctets

网络占用率计算公式:


Utilization = (ΔBytes * 8 / Δt) / dwSpeed * 100%

即,计算时间内的平均速率占理论带宽的百分比。


3.3 定时采样机制

为了实现实时刷新,我们使用:

  • Sleep() 进行时间间隔;

  • GetTickCount64() 或 QueryPerformanceCounter() 计算精确时间;

  • 每隔 N 秒重新采样,计算差值。


四、实现思路详细介绍

  1. 调用 GetIfTable() 获取系统网卡信息;

  2. 输出接口列表与带宽;

  3. 保存初始的字节计数;

  4. 等待一段时间(如1秒);

  5. 再次调用 GetIfTable();

  6. 计算字节差值与时间差;

  7. 根据带宽计算网络占用率;

  8. 循环刷新,实时输出数据。


五、完整实现代码

/*********************************************************
 * 文件名称:NetworkUsage.cpp
 * 功能描述:VC++ 实现获取系统网络占用率
 * 编译环境:Visual Studio 2019 / VC++ 6.0
 *********************************************************/

#include <windows.h>
#include <iphlpapi.h>
#include <iostream>
#include <string>
#include <vector>

#pragma comment(lib, "iphlpapi.lib")

using namespace std;

/*********************************************************
 * 结构体:NetInterfaceInfo
 * 功能:保存每个网卡的基础信息与采样数据
 *********************************************************/
struct NetInterfaceInfo
{
    DWORD index;
    string name;
    DWORD speed;             // 网卡速率 bit/s
    ULONG inBytesPrev;
    ULONG outBytesPrev;
    double utilizationIn;    // 接收占用率 %
    double utilizationOut;   // 发送占用率 %
};

/*********************************************************
 * 函数:GetNetworkInterfaces
 * 功能:枚举系统所有网卡接口
 *********************************************************/
vector<NetInterfaceInfo> GetNetworkInterfaces()
{
    vector<NetInterfaceInfo> interfaces;

    PMIB_IFTABLE pIfTable;
    DWORD dwSize = 0;
    DWORD dwRet = GetIfTable(NULL, &dwSize, FALSE);
    pIfTable = (PMIB_IFTABLE)malloc(dwSize);

    if (GetIfTable(pIfTable, &dwSize, FALSE) == NO_ERROR)
    {
        for (DWORD i = 0; i < pIfTable->dwNumEntries; i++)
        {
            MIB_IFROW row = pIfTable->table[i];

            NetInterfaceInfo info;
            info.index = row.dwIndex;
            info.speed = row.dwSpeed;
            info.name = string((char*)row.bDescr, row.dwDescrLen);
            info.inBytesPrev = row.dwInOctets;
            info.outBytesPrev = row.dwOutOctets;
            info.utilizationIn = info.utilizationOut = 0.0;

            // 过滤掉环回和非活动接口
            if (row.dwType != IF_TYPE_SOFTWARE_LOOPBACK && row.dwOperStatus == IF_OPER_STATUS_OPERATIONAL)
                interfaces.push_back(info);
        }
    }

    free(pIfTable);
    return interfaces;
}

/*********************************************************
 * 函数:UpdateNetworkUsage
 * 功能:刷新每个接口的网络占用率
 *********************************************************/
void UpdateNetworkUsage(vector<NetInterfaceInfo>& interfaces, double intervalSeconds)
{
    PMIB_IFTABLE pIfTable;
    DWORD dwSize = 0;
    DWORD dwRet = GetIfTable(NULL, &dwSize, FALSE);
    pIfTable = (PMIB_IFTABLE)malloc(dwSize);

    if (GetIfTable(pIfTable, &dwSize, FALSE) == NO_ERROR)
    {
        for (DWORD i = 0; i < pIfTable->dwNumEntries; i++)
        {
            MIB_IFROW row = pIfTable->table[i];

            for (auto& net : interfaces)
            {
                if (net.index == row.dwIndex)
                {
                    ULONG inDelta = row.dwInOctets - net.inBytesPrev;
                    ULONG outDelta = row.dwOutOctets - net.outBytesPrev;
                    net.inBytesPrev = row.dwInOctets;
                    net.outBytesPrev = row.dwOutOctets;

                    // 计算速率(bit/s)
                    double inRate = (inDelta * 8.0) / intervalSeconds;
                    double outRate = (outDelta * 8.0) / intervalSeconds;

                    net.utilizationIn = (inRate / net.speed) * 100.0;
                    net.utilizationOut = (outRate / net.speed) * 100.0;
                }
            }
        }
    }

    free(pIfTable);
}

/*********************************************************
 * 主函数:实时显示网络占用率
 *********************************************************/
int main()
{
    cout << "===== VC++ 获取网络占用率示例 =====" << endl;

    vector<NetInterfaceInfo> nets = GetNetworkInterfaces();
    if (nets.empty())
    {
        cout << "未检测到有效网络接口!" << endl;
        return 0;
    }

    cout << "检测到 " << nets.size() << " 个网卡接口:" << endl;
    for (auto& n : nets)
        cout << "接口:" << n.name << " 速率:" << n.speed / 1000000 << " Mbps" << endl;

    cout << "\n开始实时监控网络占用率(按 Ctrl+C 退出)..." << endl;

    while (true)
    {
        double interval = 1.0; // 1秒间隔
        Sleep((DWORD)(interval * 1000));

        UpdateNetworkUsage(nets, interval);

        system("cls");
        cout << "当前网络使用情况:" << endl;
        for (auto& n : nets)
        {
            cout << "接口:" << n.name
                 << " | 下行:" << n.utilizationIn << "%"
                 << " | 上行:" << n.utilizationOut << "%"
                 << " | 总占用:" << n.utilizationIn + n.utilizationOut << "%" << endl;
        }
    }

    return 0;
}

六、代码详细解读

  1. GetNetworkInterfaces()

    • 调用 GetIfTable() 枚举所有网卡;

    • 过滤非活动与环回接口;

    • 保存每个网卡的速率、字节计数。

  2. UpdateNetworkUsage()

    • 再次调用 GetIfTable() 获取实时流量数据;

    • 计算上次与本次之间的差值;

    • 根据间隔时间计算速率;

    • 根据带宽计算占用率。

  3. main()

    • 初始化网卡列表;

    • 每隔 1 秒刷新一次;

    • 实时输出网络占用率。


七、项目详细总结

本项目通过 VC++ 的 IP Helper API 实现了获取系统网络占用率的功能。
项目完整地涵盖了以下核心知识点:

  1. 网络接口信息枚举;

  2. 字节统计与速率计算;

  3. 实时刷新与控制台显示;

  4. 数据采样与时间差计算;

  5. 网络带宽占用率计算原理。

该实现思路简单、稳定,特别适合嵌入系统监控程序或自定义网络分析工具中。

运行结果示例(1秒采样):


当前网络使用情况: 接口:Realtek PCIe GbE Family Controller | 下行:2.35% | 上行:0.84% | 总占用:3.19% 接口:Intel Wi-Fi 6 AX200 160MHz | 下行:0.12% | 上行:0.04% | 总占用:0.16%


八、项目常见问题及解答

Q1:为什么所有网卡显示为0%?
A1:可能当前没有网络传输流量,请尝试下载或播放视频以产生数据。

Q2:如何让刷新更快?
A2:减少采样间隔,例如设为 0.5 秒;但会增加 CPU 占用。

Q3:为什么速率单位是比特?
A3:因为带宽是以 bit/s 表示的,所以计算中需乘以 8(Byte→bit)。

Q4:支持无线网卡和虚拟网卡吗?
A4:支持,只要系统识别为网络接口即可。

Q5:如何在 GUI 程序中显示?
A5:可将结果绑定到 MFC 对话框控件上,定时刷新显示。


九、扩展方向与性能优化

  1. 多线程刷新机制

    • 将监控线程与UI分离,提高响应速度。

  2. 数据图形化展示

    • 使用 GDI+ 绘制实时曲线图表。

  3. 带宽自适应

    • 自动检测接口最大速率,动态计算百分比。

  4. 日志记录功能

    • 定期保存网络使用率到日志文件,便于长期分析。

  5. 网络包捕获扩展

    • 使用 WinPcap/Npcap 捕获真实包流,进行更精准统计。

  6. 跨平台实现

    • 在 Linux 下使用 /proc/net/dev 或 getifaddrs 实现同样功能。

  7. 性能优化

    • 使用 GetIfEntry2(新API)获取更精确的64位计数器;

    • 使用异步事件代替 Sleep,提高采样稳定性。

更多推荐