linux 下读 /proc/net/dev 是最轻量可靠方式,跳过前两行后逐行解析:接口名去冒号,第2列为 rx_bytes,第10列为 tx_bytes,间隔≥100ms;windows 用 getiftable2() 更优。

Linux 下直接读 /proc/net/dev 是最轻量、最可靠的方式;Windows 需用 GetIfTable2() 或 WMI,但前者延迟低、权限要求低。
Linux:解析 /proc/net/dev 获取实时收发字节数
这个文件每秒更新,无需 root 权限,字段稳定(从内核 2.6 起格式未变)。关键不是“怎么读”,而是“怎么避免解析错”:
- 跳过前两行(表头和分隔线),从第三行开始逐行处理
- 接口名在每行开头,可能带冒号(如
eth0:),记得trim掉末尾冒号 - 第 2 列是接收字节数(
rx_bytes),第 10 列是发送字节数(tx_bytes)——注意列索引从 1 开始计数 - 两次读取间隔建议 ≥ 100ms,太短会导致差值为 0(内核未更新统计)
示例片段(C++17):
std::ifstream dev{"/proc/net/dev"};
std::string line;
int line_no = 0;
while (std::getline(dev, line)) {
if (line_no++ > iface;
if (iface.empty()) continue;
if (iface.back() == ':') iface.pop_back();
uint64_t rx_bytes, tx_bytes;
// 跳过前1列(iface),读第2列(rx_bytes),再跳过6列,读第10列(tx_bytes)
for (int i = 0; i > rx_bytes;
for (int i = 0; i > std::string{};
iss >> tx_bytes;
std::cout
<h3>Windows:用 <code>GetIfTable2()</code> 替代已废弃的 <code>GetIfTable()</code>
</h3>
<p><code>GetIfTable()</code> 返回的 <code>MIB_IFROW</code> 中的 <code>dwInOctets</code>/<code>dwOutOctets</code> 在现代 Windows(Win10 1809+)上常为 0 或不更新;必须用 <code>GetIfTable2()</code> + <code>PMIB_IF_ROW2</code>:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 需链接
iphlpapi.lib,且最低支持 Windows 7 SP1,但推荐 Win10 1803+ - 返回结构体中
InOctets和OutOctets是UINT64,直接对应字节数,无溢出风险 - 调用前需
InitializeCriticalSection()?不需要——该函数线程安全,但频繁调用(如 100ms 一次)会触发内核查询,建议缓存IF_INDEX后复用GetIfEntry2() - 注意:某些虚拟网卡(如 Hyper-V、WSL2 vEthernet)可能上报 0 值,应过滤掉
InterfaceLuid.Value == 0的条目
跨平台封装时,别碰 libpcap 或 netstat 命令
这两个方案看似通用,实则埋坑:
-
libpcap抓包统计的是经过应用层的包,不含内核直接丢弃的(如防火墙 DROP)、也不含 TCP 重传包,数值显著低于/proc/net/dev或GetIfTable2() - 调用
netstat -i或ip -s link解析输出——字段顺序随 locale 变化(如德语系统列名是Empfangen),且命令执行开销大(fork + exec),1 秒内多次调用会拖慢主逻辑 - 真正跨平台的最小公约数,就是 Linux 读文件、Windows 调系统 API,用宏
#ifdef _WIN32分支即可,硬凑统一接口反而增加维护成本
差值计算与单位换算的易错点
流量监控的核心不是“绝对值”,而是“Δbytes / Δt”。这里最容易忽略的是:
- 两次读取时间戳必须用高精度时钟(
std::chrono::steady_clock),不能用time()或GetTickCount()(后者 15ms 精度不够) - 字节数差值可能因网卡重置或内核统计翻转而突降(尤其 USB 网卡),需检测负值并视为重置,清零累计值
- 显示时按 1000 进制(kbps、Mbps)而非 1024 进制——运营商和网卡驱动都按十进制算,混淆会导致显示值比实际高约 2.4%
- 不要每秒都打印全部接口:只输出变化 > 1KB/s 的接口,静默其余,避免日志刷屏
真实场景里,/proc/net/dev 的字段对齐空格数量不固定,用 std::istringstream 比正则或 strtok 更稳;Windows 下 GetIfTable2() 返回的接口名是 Unicode,但 Alias 字段可能为空,优先用 Description 或 Luid 做唯一标识——这些细节不踩一遍很难记住。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










