原创作者:郑同学的笔记
原文链接:https://zhengjunxue.blog.csdn.net/article/details/137550822

一、网络速率和网络占用率

在网络术语中,网络速率和网络占用率是两个不同的概念:

1、网络速率(Network Rate):

  • 定义:网络速率通常指的是网络连接的最大数据传输速度,即单位时间内网络可以传输的数据量。它的计量单位通常是比特每秒(bits per second,bps),常见的还有千比特每秒(Kbps)、兆比特每秒(Mbps)和吉比特每秒(Gbps)等。

  • 实例:如果你的互联网服务提供商承诺提供100 Mbps的下载速率,这意味着理想情况下,你的网络连接理论上可以在一秒内传输最多100兆比特的数据。

网络速率 = 单位时间网络流量 / 时间

2、网络占用率(Network Utilization):

  • 定义:网络占用率则指的是在某一时间点或时间段内,网络的实际传输数据量占网络最大传输能力的比例。它是对网络资源被使用的程度的度量,通常以百分比形式表示。

  • 实例:如果网络的最大带宽为1 Gbps,而目前实际传输的数据量为500 Mbps,则网络占用率为50%。

网络占用率 = 网络速率/网络带宽

所以,你想计算网络占用率,只需要把我们本篇计算的网络速率计算出来,然后除以你的网络带宽,就是网络占用率了。

二、计算网络速率的方法_windows

1、windows

  • PDH(Performance Data Helper):
    PDH 是 Windows 中用于收集性能计数器数据的API。通过 PDH,你可以编程方式访问系统性能数据,包括网络接口的传输速率。你可以使用 PDH API 来编写自己的应用程序或脚本来监视网络速率。
    使用 PDH API,你可以编写一些自定义的代码来查询性能计数器数据,包括网络接口的传输速率。这个方法需要一定的编程技能,但提供了更灵活和定制化的方式来监视网络性能。

  • IP Helper:
    IP Helper 是一个 Windows API,提供了许多函数来查询和管理网络配置信息。虽然 IP Helper 主要用于获取和设置 IP 地址、路由表、网络接口信息等,但也可以用于监视网络接口的状态和性能。
    通过 IP Helper,你可以编写自己的程序来查询网络接口的信息,包括当前的连接状态、速率等。虽然 IP Helper 不像 PDH 那样专门用于性能监视,但它可以提供一些相关的网络信息。

2、PDH调用接口流程:

  • 在计算网络速率时,pdh(Performance Data Helper)是一个Windows Performance Counters(性能计数器)的API,用于访问和操作性能计数器数据,包括但不限于处理器利用率、内存使用情况以及网络接口的统计信息。
  1. 初始化 PDH 查询对象:PdhOpenQuery();
  2. 添加网络接口速率计数器:PdhAddCounter();
  3. 收集计数器数据: PdhCollectQueryData();
  4. 等待时间,如sleep,比如1s
  5. 再次,收集计数器数据: PdhCollectQueryData();
  6. 获取计数器的当前值(即速率: 这里的单位是字节/秒 (Bytes/second) ):PdhGetFormattedCounterValue();
  7. 计算并输出速率(转换为适当的单位,如 Kbps、Mbps):
#include <pdh.h>
#include <pdhmsg.h>
#pragma comment(lib, "pdh.lib")

// 示例代码简化版,不完整,仅为示意
void getNetworkUsage()
{
    PDH_HQUERY queryHandle;
    PDH_HCOUNTER counterHandle;
    PDH_FMT_COUNTERVALUE counterValue;

    // 创建查询句柄
    PdhOpenQuery(NULL, NULL, &queryHandle);

    // 添加网络接口计数器,例如“\\Network Interface(*)\\Bytes Sent/sec”
    PdhAddCounter(queryHandle, "\\Network Interface(*)\\Bytes Sent/sec", NULL, &counterHandle);

    // 更新计数器值
    PdhCollectQueryData(queryHandle);

    // 等待合适的时间间隔后再次收集数据(例如Sleep函数)
    Sleep(1000);

    // 收集第二次数据
    PdhCollectQueryData(queryHandle);

    // 获取速率值
    PdhGetFormattedCounterValue(counterHandle, PDH_FMT_DOUBLE, NULL, &counterValue);
    double sentRate = counterValue.doubleValue;

    // 类似地,获取接收速率(例如:“\\Network Interface(*)\\Bytes Received/sec”)

    // 关闭查询句柄
    PdhCloseQuery(queryHandle);

    // 根据实际情况计算网络占用率,例如:(sentRate + receivedRate) / 2
    // 此处的计算取决于具体的定义和需求
}

3、使用IP Helper库

使用使用IP Helper库需要统计,太麻烦了,我们还是采用pdh来实现。

// 或者使用IP Helper库
#include <iphlpapi.h>
#include <winsock2.h>
#include <ws2tcpip.h>

void getNetworkUsageIPHLPAPI()
{
    MIB_IFTABLE* pIfTable = NULL;
    ULONG dwSize = 0;

    // 获取所需的缓冲区大小
    GetIfTable(pIfTable, &dwSize, FALSE);
    pIfTable = (MIB_IFTABLE*)malloc(dwSize);
    
    // 获取网络接口表
    if (GetIfTable(pIfTable, &dwSize, TRUE) == NO_ERROR)
    {
        for (DWORD i = 0; i < pIfTable->dwNumEntries; ++i)
        {
            MIB_IFROW ifRow = pIfTable->table[i];
            ULONG64 bytesSent = ifRow.dwOutOctets;
            ULONG64 bytesReceived = ifRow.dwInOctets;

            // 计算速率(假设有两次采样,可以根据时间间隔来计算)
            // ...
        }
        
        free(pIfTable);
    }
}

三、计算网络速率的方法——linux

Linux环境:
原理: 在Linux中,可以读取 /proc/net/dev 文件获取网络接口的统计信息。

接口与参数:

Cpp
#include <fstream>
#include <iostream>
#include <regex>
#include <chrono>

void getNetworkUsageLinux()
{
    using namespace std::chrono_literals;
    auto start = std::chrono::steady_clock::now();

    // 等待合适的时间间隔
    std::this_thread::sleep_for(1s);

    auto end = std::chrono::steady_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::seconds>(end - start).count();

    std::ifstream ifs("/proc/net/dev");
    std::string line;
    std::smatch match;

    // 查找指定接口,例如 "eth0"
    std::string interfaceName = "eth0";

    while (std::getline(ifs, line))
    {
        if (std::regex_search(line, match, std::regex(interfaceName + ":")))
        {
            std::istringstream iss(line);
            std::string discard;
            iss >> discard >> discard >> discard; // 跳过前三列

            uint64_t prevRecv, prevSend, currRecv, currSend;
            iss >> prevRecv >> prevSend; // 读取前一时刻的接收和发送字节数

            // 休息一段时间后重新读取
            ifs.clear();
            ifs.seekg(0);
            std::getline(ifs, line);
            std::istringstream iss2(line);
            iss2 >> discard >> discard >> discard;
            iss2 >> currRecv >> currSend; // 读取当前时刻的接收和发送字节数

            // 计算速率(假设字节/秒)
            double recvRate = (currRecv - prevRecv) / static_cast<double>(duration);
            double sendRate = (currSend - prevSend) / static_cast<double>(duration);

            // 根据实际情况计算网络占用率
            // ...
        }
    }
}

四、使用pdh的完整demo

#include <iostream>  
#include <pdh.h>
#include <pdhmsg.h>
#pragma comment(lib, "pdh.lib")

using namespace std;
// 示例代码简化版,
int getNetworkUsage(double &sentRate,double &receivedRate)
{
    // 创建查询句柄
    PDH_HQUERY queryHandle = NULL; //查询句柄
    PDH_STATUS pdhStatus = PdhOpenQuery(NULL, 0, &queryHandle);
    if (ERROR_SUCCESS != pdhStatus)
    {
        cout << "Failed to open query.\n";
        return -1;
    }

    //实际网络接口名称,如 "Network Interface(Intel[R] Wi-Fi 6E AX211 160MHz)",具体可以手动建立一个性能计数器,看下名字
    // 发送网络的性能计数器路径、接收网络的性能计数器路径
    LPCSTR sentPath = "\\Network Interface(Intel[R] Wi-Fi 6E AX211 160MHz)\\Bytes Sent/sec";
    LPCSTR recvPath = "\\Network Interface(Intel[R] Wi-Fi 6E AX211 160MHz)\\Bytes Received/sec";

    //添加到查询的计数器的句柄。 可能需要在后续调用中引用此句柄。
    PDH_HCOUNTER sentCounterHandle = NULL;
    PDH_HCOUNTER receivedCounterHandle = NULL;
    // 添加发送字节计数器
    pdhStatus = PdhAddCounter(queryHandle, sentPath, NULL, &sentCounterHandle);
    if (ERROR_SUCCESS != pdhStatus)
    {
        cout << "Error adding Bytes Sent/sec counter.\n";
        goto cleanup;
        return -1;
    }

    // 添加接收字节计数器
    pdhStatus = PdhAddCounter(queryHandle, recvPath, NULL, &receivedCounterHandle);
    if (ERROR_SUCCESS != pdhStatus)
    {
        cout << "Error adding Bytes Received/sec counter.\n";
        return -1;
    }

    int flag = 0;

    while(1)
    {
        if (flag == 0)
        {
            // 第一次,更新计数器值
            pdhStatus = PdhCollectQueryData(queryHandle);
            if (ERROR_SUCCESS != pdhStatus)
            {
                cout << "Error collecting data.\n";
                goto cleanup;
                return -1;
            }
            flag++;
        }
        
        // 等待合适的时间间隔后再次收集数据(例如Sleep函数)
        Sleep(1000);

        // 假设在此处等待一段时间,然后再次收集数据以得到差值
        // 收集第二次数据
        pdhStatus = PdhCollectQueryData(queryHandle);
        if (ERROR_SUCCESS != pdhStatus)
        {
            cout << "Error collecting data.\n";
            goto cleanup;
            return -1;
        }


        //接收计数器值的 PDH_FMT_COUNTERVALUE 结构
        PDH_FMT_COUNTERVALUE sentCounterValue, receivedCounterValue;
        // 获取发送速率值
        pdhStatus = PdhGetFormattedCounterValue(sentCounterHandle, PDH_FMT_DOUBLE, NULL, &sentCounterValue);
        if (ERROR_SUCCESS != pdhStatus)
        {
            cout << "Error getting Bytes sent/sec value.\n";
            goto cleanup;
            return -1;
        }
        /*double */sentRate = sentCounterValue.doubleValue;// 这里的单位是字节/秒 (Bytes/second)
        printf("Bytes sent/sec: %lf\n", sentRate);

        // 获取接收速率值
        pdhStatus = PdhGetFormattedCounterValue(receivedCounterHandle, PDH_FMT_DOUBLE, NULL, &receivedCounterValue);
        if (ERROR_SUCCESS != pdhStatus)
        {
            cout << "Error getting Bytes Received/sec value.\n";
            goto cleanup;
            return -1;
        }
        /*double */receivedRate = receivedCounterValue.doubleValue; // 这里的单位是字节/秒 (Bytes/second)
        printf("Bytes Received/sec: %lf\n", receivedRate);

        // 根据实际情况计算网络占用率,例如,这里简单的将发送和接收速率相加求平均
        double networkUsage = (sentRate + receivedRate) / 2;

        // 实际的网络占用率计算方法可能需要根据你的具体需求调整
        printf("sent Kbps: %lf\n", sentRate*8/1000); // 转换成兆比特/秒 (Kbps)
        printf("sent Mbps: %lf\n", sentRate * 8 / 1000000); // 转换成兆比特/秒 (Kbps)

        printf("Received Kbps: %lf\n", receivedRate * 8 / 1000);// 转换成兆比特/秒 (Mbps)
        printf("Received Mbps: %lf\n", receivedRate*8/1000000);// 转换成兆比特/秒 (Mbps)
    }
    // 关闭查询句柄
    goto cleanup;

    cleanup:
        // 清理资源
        if (queryHandle) PdhCloseQuery(queryHandle);
        if (sentCounterHandle) PdhRemoveCounter(sentCounterHandle);
        if (receivedCounterHandle) PdhRemoveCounter(receivedCounterHandle);       
}

int main() 
{
    // 初始化  
    double sentVale = 0, receivedVale = 0;
    getNetworkUsage(sentVale, receivedVale);
    return 0;
}

输出
在这里插入图片描述

五、关于Kbps和KB/s以及Mbps和MB/s

对于上面转换分不清楚的同学,可以查看我早期的一篇博客
串口通信和以太网通信100Mbps速度的对比

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐