linux下读取/proc/net/dev获取网卡流量需两次采样求差并除以时间间隔换算为mbps,注意跳过header、正确提取第2和第10列字节数;windows用getiftable2获取累计值,需初始化、处理ifindex不稳定性及64位整数接收。

Linux 下读取 /proc/net/dev 获取实时网卡流量
Linux 系统最可靠、开销最低的方式就是解析 /proc/net/dev。它每秒更新,内容是各网卡的累计收发字节数(单位:字节),不是瞬时速率——你需要两次采样做差,再除以时间间隔换算成 Mbps。
常见错误是直接读一次就计算,结果为 0 或极大噪声值;另一坑是忽略字段对齐:/proc/net/dev 第一行是 header,第二行开始才是数据,且前两列是 interface 名(可能含空格,如 ens33 或 docker0),后续每两列分别是 receive 和 transmit 的字节数、包数等,真正要用的是第 2 列(接收字节)和第 10 列(发送字节)。
实操建议:
- 用
std::ifstream按行读取,跳过首行 header;对每行用std::istringstream提取字段,按空格分割后过滤掉空字符串,确保取到第 2 和第 10 个有效数值(索引从 0 开始) - 选网卡名要明确,别硬写
"eth0"—— 先读/proc/sys/net/ipv4/conf/all/forwarding或用getifaddrs()找默认路由接口,或由用户传参指定 - 两次采样间隔建议 ≥ 100ms,太短会导致除零或精度失真;换算公式:
(bytes_diff * 8.0) / (interval_ms * 1000.0)→ 单位 Mbps
Windows 上用 GetIfTable2 获取每秒流量
Windows 没有类似 /proc 的伪文件系统,必须调用网络管理 API。GetIfTable2 是推荐方式(Win8+),返回结构体数组,每个元素含 InOctets 和 OutOctets 字段,同样是累计值。
容易踩的坑:链接时需加 iphlpapi.lib,且必须在代码开头调用 InitializeNetworkInterface 类初始化(实际是调 LoadLibrary 加载 iphlpapi.dll);另一个问题是接口索引(IfIndex)不稳定,重启可能变化,不能硬编码,得先用 GetIfEntry2 或遍历表匹配 Alias(如 "以太网")或 Luid。
实操建议:
- 用
NET_LUID+ConvertInterfaceLuidToNameW做名称映射,比字符串匹配更健壮 - 注意
GetIfTable2返回的ULONG64是 64 位整数,C++ 中务必用uint64_t接收,避免截断 - 采样逻辑和 Linux 一致:缓存上一次的
InOctets/OutOctets,间隔后相减再转 Mbps;Windows 高精度计时推荐QueryPerformanceCounter
C++ 跨平台封装要点:避免重复解析与线程安全
如果想写一次代码跑两边,别用宏条件编译把整个函数拆开——那样难测难维护。应该抽象出统一接口,比如 struct NetworkStats { uint64_t rx_bytes; uint64_t tx_bytes; };,然后按平台实现两个私有函数 get_stats_linux() 和 get_stats_windows()。
关键复杂点在于:流量统计必须连续采样才有意义,但用户调用 get_current_mbps() 期望返回“当前速率”,这隐含状态管理。直接每次重读会丢失上下文。
实操建议:
- 内部持有一个
std::mutex和缓存结构体(含上次字节数、上次时间戳),首次调用返回 0,第二次才出有效值 - 不要在 getter 里 sleep 或阻塞等待——把采样节奏交给使用者控制,提供
update()主动刷新 +get_mbps()只读取 - 若需多线程并发访问,
std::atomic<uint64_t></uint64_t>不够(要同时更新 rx/tx/timestamp 三个量),必须用互斥锁保护整个结构体
为什么不用 libpcap 或 netlink?
libpcap 抓包能统计流量,但它是旁路监听,不经过协议栈,会漏掉本地 loopback 流量,且性能开销大(需复制每个包)、权限要求高(root/admin);netlink(Linux)虽可订阅流量事件,但需要自己处理 socket、消息解析、多播组加入,远不如读 /proc/net/dev 简单稳定。
真实场景下,监控类工具(如 iftop、nload)底层都是轮询 /proc/net/dev;Windows 的 Performance Counter(如 "Network Interface\Bytes Received/sec")也可用,但它依赖 WMI 或 PDH,启动慢、延迟高、部分容器环境不可用。
所以除非你要做深度包检测,否则坚持用 OS 提供的累计计数器 + 差分法——这是唯一兼顾准确性、兼容性和低侵入性的方案。
真正麻烦的从来不是读哪个文件或调哪个函数,而是怎么让两次采样之间的时间间隔足够准、字节数不溢出、网卡热插拔时状态不崩——这些细节藏在循环逻辑和异常分支里,不是贴几行代码就能绕过的。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











