如何获取带宽使用情况?如何通过Windows API计算设备带宽使用率百分比
获取Windows设备带宽使用情况及计算使用率(Windows API实现)
为什么GetIpStatisticsEx()不适用?
GetIpStatisticsEx()仅能获取IPv4/IPv6的全局统计数据(比如总发送/接收字节数),无法拿到单网卡的实时流量数据,也没法直接计算瞬时带宽使用率,所以需要换用更适配的API方案。
方案一:基于IP Helper API的周期性采样
这是获取网卡实时流量的常用方式,核心思路是通过两次采样的字节差计算瞬时带宽,再结合网卡标称带宽得出使用率。
实现步骤
- 调用
GetIfTable2()获取所有网卡的接口列表,提取目标网卡的IF_INDEX(接口索引)。 - 每隔固定时间(如1秒)调用
GetIfEntry2(),记录当前网卡的InOctets(接收总字节数)和OutOctets(发送总字节数)。 - 计算两次采样的字节差值,除以时间间隔得到瞬时带宽(字节/秒)。
- 将瞬时带宽转换为比特单位后,除以网卡标称带宽(
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
相关产品推荐
相关产品推荐

