Linux统计网卡流量
作者:mmseoamin日期:2023-12-14

cat /proc/net/dev

Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。用户和应用程序可以通过proc得到系统的信息,并可以改变内核的某些参数。由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是动态从系统内核读出所需信息并提交的。

/proc文件系统中包含了很多目录,其中/proc/net/dev就是提供给用户读取或更改网络适配器及统计信息的途径。

注意:因为proc是伪文件系统,只存在内存中,所以这里统计的数据的时间起止时间是:系统启动到命令执行,如果此时系统发生重启,数据将会清零。

Linux统计网卡流量,在这里插入图片描述,第1张

参数说明:

bytes: 接口发送或接收的数据的总字节数

packets: 接口发送或接收的数据包总数

errs: 由设备驱动程序检测到的发送或接收错误的总数

drop: 设备驱动程序丢弃的数据包总数

fifo: FIFO缓冲区错误的数量

frame: 分组帧错误的数量

colls: 接口上检测到的冲突数

compressed: 设备驱动程序发送或接收的压缩数据包数

carrier: 由设备驱动程序检测到的载波损耗的数量

multicast: 设备驱动程序发送或接收的多播帧数

其实,我们平时经常用的很多查看网卡实时流量的命令,都是通过读取该目录下的实时流量,并通过简单计算得到的。

#include 
#include 
#include 
#include 
#include 
#include 
// 更改为你要监测的网卡名称,可以通过ifconfig获得
#define INTERFACE_NAME "ens33"
unsigned long long ticBytes;
unsigned long long tocBytes;
// 读取指定网卡的统计信息
int readInterfaceStats(const char *interfaceName, bool isTic) {
    FILE *file = fopen("/proc/net/dev", "r");
    if (file == NULL) {
        printf("Failed to open /proc/net/dev");
        return -1;
    }
    char line[256];
    while (fgets(line, sizeof(line), file)) {
        if (strstr(line, interfaceName) != NULL) {
            if (isTic) {
                // 读取bytes的相关信息,用%*s跳过无用字符串
                sscanf(line, "%*s %llu", &ticBytes);
            }
            else {
                sscanf(line, "%*s %llu", &tocBytes);
            }
            fclose(file);
            return 0;
        }
    }
    fclose(file);
    return -1;
}
long *myIfconfig(char *interfaceName)
{
    int fd = open("/proc/net/dev", O_RDONLY | O_EXCL);
    if (-1 == fd)
    {
        printf("/proc/net/dev not exists!\n");
        return NULL;
    }
    char buf[1024*2];
    lseek(fd, 0, SEEK_SET);
    int nBytes = read(fd, buf, sizeof(buf)-1);
    if (-1 == nBytes)
    {
        printf("read error");
        close(fd);
        return NULL;
    }
    buf[nBytes] = '\0';
    close(fd);
//返回第一次指向netCard位置的指针
    char* pDev = strstr(buf, interfaceName);
    if (NULL == pDev)
    {
        printf("don't find dev %s\n", interfaceName);
        return NULL;
    }
    char *p;
    char *ifconfigValue;
    int i = 0;
    static long rx2Tx10[2];
/*去除空格,制表符,换行符等不需要的字段*/
    for (p = strtok(pDev, " \t\r\n"); p; p = strtok(NULL, " \t\r\n"))
    {
        i++;
        ifconfigValue = (char*)malloc(20);
        strcpy(ifconfigValue, p);
/*得到的字符串中的第二个字段是接收流量*/
        if(i == 2)
        {
            rx2Tx10[0] = atol(ifconfigValue);
        }
/*得到的字符串中的第十个字段是发送流量*/
        if(i == 10)
        {
            rx2Tx10[1] = atol(ifconfigValue);
            break;
        }
        free(ifconfigValue);
    }
    return rx2Tx10;
}
int main()
{
    double interval = 1.0; // 采样间隔,单位为秒
    while (1) {
        if (readInterfaceStats(INTERFACE_NAME, true) == -1) {
            printf("Failed to read interface stats\n");
            return -1;
        }
        sleep((unsigned int)interval);
        if (readInterfaceStats(INTERFACE_NAME, false) == -1) {
            printf("Failed to read interface stats\n");
            return -1;
        }
        double kbps = (tocBytes - ticBytes) / interval / 1024;
        printf("receive speed is Kb/s: %.2f\n", kbps);
        long *ifconfigResult;
        double reKb;
        ifconfigResult = myIfconfig(INTERFACE_NAME);
        reKb = (double)ifconfigResult[0] / (1024);
        printf("Receive total: %0.2f KB\n", reKb);
        reKb = (double)ifconfigResult[1] / (1024);
        printf("Send total: %0.2f KB\n", reKb);
    }
    return 0;
}