You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取带宽使用情况?如何通过Windows API计算设备带宽使用率百分比

获取Windows设备带宽使用情况及计算使用率(Windows API实现)

为什么GetIpStatisticsEx()不适用?

GetIpStatisticsEx()仅能获取IPv4/IPv6的全局统计数据(比如总发送/接收字节数),无法拿到单网卡的实时流量数据,也没法直接计算瞬时带宽使用率,所以需要换用更适配的API方案。

方案一:基于IP Helper API的周期性采样

这是获取网卡实时流量的常用方式,核心思路是通过两次采样的字节差计算瞬时带宽,再结合网卡标称带宽得出使用率。

实现步骤

  1. 调用GetIfTable2()获取所有网卡的接口列表,提取目标网卡的IF_INDEX(接口索引)。
  2. 每隔固定时间(如1秒)调用GetIfEntry2(),记录当前网卡的InOctets(接收总字节数)和OutOctets(发送总字节数)。
  3. 计算两次采样的字节差值,除以时间间隔得到瞬时带宽(字节/秒)。
  4. 将瞬时带宽转换为比特单位后,除以网卡标称带宽(Speed字段,单位bps),最终得到使用率百分比。

示例代码片段

#include <iphlpapi.h>
#include <windows.h>
#include <iostream>
#include <chrono>

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

void CalculateBandwidth(DWORD ifIndex) {
    MIB_IF_ROW2 ifRow = {0};
    ifRow.InterfaceIndex = ifIndex;

    // 第一次采样
    if (GetIfEntry2(&ifRow) != NO_ERROR) {
        std::cerr << "GetIfEntry2调用失败" << std::endl;
        return;
    }
    ULONGLONG prevIn = ifRow.InOctets;
    ULONGLONG prevOut = ifRow.OutOctets;
    auto prevTime = std::chrono::steady_clock::now();

    Sleep(1000);

    // 第二次采样
    if (GetIfEntry2(&ifRow) != NO_ERROR) {
        std::cerr << "GetIfEntry2调用失败" << std::endl;
        return;
    }
    ULONGLONG currIn = ifRow.InOctets;
    ULONGLONG currOut = ifRow.OutOctets;
    auto currTime = std::chrono::steady_clock::now();

    // 计算时间差与带宽
    double elapsed = std::chrono::duration<double>(currTime - prevTime).count();
    double inBps = (currIn - prevIn) / elapsed * 8;
    double outBps = (currOut - prevOut) / elapsed * 8;
    double totalBps = inBps + outBps;

    // 计算使用率
    ULONGLONG nicSpeed = ifRow.Speed;
    if (nicSpeed == 0) {
        std::cerr << "无法获取网卡标称带宽" << std::endl;
        return;
    }
    double usage = (totalBps / nicSpeed) * 100;

    std::cout << "总带宽使用率: " << usage << "%" << std::endl;
    std::cout << "下行: " << inBps / 1024 / 1024 << " Mbps" << std::endl;
    std::cout << "上行: " << outBps / 1024 / 1024 << " Mbps" << std::endl;
}

int main() {
    PMIB_IF_TABLE2 pIfTable = nullptr;
    DWORD ret = GetIfTable2(&pIfTable);
    if (ret != NO_ERROR) {
        std::cerr << "GetIfTable2调用失败" << std::endl;
        return 1;
    }

    // 筛选启用状态的非回环网卡
    for (DWORD i = 0; i < pIfTable->NumEntries; i++) {
        MIB_IF_ROW2* row = &pIfTable->Table[i];
        if (row->OperStatus == IfOperStatusUp && row->InterfaceType != IF_TYPE_LOOPBACK) {
            wprintf(L"使用网卡: %s\n", row->Alias);
            CalculateBandwidth(row->InterfaceIndex);
            break;
        }
    }

    if (pIfTable) FreeMibTable(pIfTable);
    return 0;
}

方案二:使用Windows性能计数器

Windows性能计数器提供了封装好的系统性能数据查询接口,无需手动处理采样逻辑,直接读取网卡的流量和带宽数据即可。

核心逻辑

  • 计数器路径\Network Interface(*)\Bytes Total/sec:获取网卡每秒总收发字节数
  • 计数器路径\Network Interface(*)\Current Bandwidth:获取网卡标称带宽(单位bps)
  • 使用率计算公式:(Bytes Total/sec * 8) / Current Bandwidth * 100%

示例代码片段

#include <windows.h>
#include <pdh.h>
#include <iostream>

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

void GetBandwidthUsage(const wchar_t* nicName) {
    PDH_HQUERY hQuery = nullptr;
    PDH_HCOUNTER hTotalBytes = nullptr;
    PDH_HCOUNTER hBandwidth = nullptr;

    if (PdhOpenQuery(nullptr, 0, &hQuery) != ERROR_SUCCESS) {
        std::cerr << "PdhOpenQuery调用失败" << std::endl;
        return;
    }

    // 构造计数器路径
    wchar_t pathTotal[256];
    swprintf_s(pathTotal, L"\\Network Interface(%s)\\Bytes Total/sec", nicName);
    wchar_t pathBandwidth[256];
    swprintf_s(pathBandwidth, L"\\Network Interface(%s)\\Current Bandwidth", nicName);

    if (PdhAddCounter(hQuery, pathTotal, 0, &hTotalBytes) != ERROR_SUCCESS ||
        PdhAddCounter(hQuery, pathBandwidth, 0, &hBandwidth) != ERROR_SUCCESS) {
        std::cerr << "PdhAddCounter调用失败" << std::endl;
        PdhCloseQuery(hQuery);
        return;
    }

    // 初始化采样
    PdhCollectQueryData(hQuery);
    Sleep(1000);
    PdhCollectQueryData(hQuery);

    // 获取计数器值
    PDH_FMT_COUNTERVALUE valTotal;
    PDH_FMT_COUNTERVALUE valBandwidth;
    if (PdhGetFormattedCounterValue(hTotalBytes, PDH_FMT_DOUBLE, nullptr, &valTotal) != ERROR_SUCCESS ||
        PdhGetFormattedCounterValue(hBandwidth, PDH_FMT_LARGE, nullptr, &valBandwidth) != ERROR_SUCCESS) {
        std::cerr << "PdhGetFormattedCounterValue调用失败" << std::endl;
        PdhCloseQuery(hQuery);
        return;
    }

    double usage = (valTotal.doubleValue * 8 / valBandwidth.largeValue) * 100;
    std::cout << "带宽使用率: " << usage << "%" << std::endl;

    PdhCloseQuery(hQuery);
}

int main() {
    // 替换为你的网卡名称(可在任务管理器-性能页查看)
    GetBandwidthUsage(L"Intel(R) Ethernet Connection I219-V");
    return 0;
}

注意事项

  • GetIfEntry2()返回的InOctets/OutOctets是64位值,不会出现溢出问题,适合长期监控场景。
  • 性能计数器方式更简洁,但需要准确的网卡名称,不同设备的网卡名称可能存在差异。
  • 计算时注意单位转换:字节转比特需乘以8,网卡标称带宽单位为bps。

内容的提问来源于stack exchange,提问作者Ahmad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 10:55:17