linux通过解析/proc/net/dev中网卡tx_bytes字段(第9列)计算出口带宽,需跳过表头、校验空白行、防并发读取不完整;windows调用getifentry2获取outoctets,须先转换接口名到luid;两者均需处理计数器重置与溢出,采样间隔≥100ms。

Linux下用/proc/net/dev读取网卡出口字节数增量
Linux没有直接API返回“出口带宽”,得靠采样/proc/net/dev里各网卡的tx_bytes字段,算单位时间增量。这个文件每行含接收(rx_bytes)和发送(tx_bytes)字节数,是内核维护的累计值。
实操注意点:
- 解析时跳过前两行(表头),按空格分割后第9列是
tx_bytes(从0开始计数,实际是第9个字段) - 需指定具体网卡名(如
eth0或wlan0),不能只看lo回环接口 - 两次读取间隔建议≥100ms,太短会导致增量为0;间隔太长(如>5s)则趋势毛刺大
- 读取后要减去上次值,再除以时间差(秒),得到B/s;除以1024²得MB/s,更符合习惯
C++用std::ifstream安全读取/proc/net/dev避免阻塞或截断
/proc/net/dev是虚拟文件,std::ifstream读取时可能因内核并发更新导致某次读到不完整行——尤其在高吞吐网卡上。不能依赖getline()一定读满整行。
稳妥做法:
- 每次打开新流(
std::ifstream ifs("/proc/net/dev")),不复用已打开的句柄 - 用
std::string line配合while (std::getline(ifs, line))逐行处理,但对每行做line.find_first_not_of(" \t") != std::string::npos校验是否为空白行 - 分割字段用
std::istringstream+skipws,比strtok更安全,避免空格数量不一致导致错位 - 遇到
tx_bytes字段非数字(如"0"以外的非法字符),跳过该行,不抛异常
Windows下用GetIfEntry2获取实时OutOctets需先调用ConvertInterfaceIndexToLuid
Windows没等价于/proc/net/dev的文本接口,必须走iphlpapi.h里的结构化API。关键字段是MIB_IF_ROW2::OutOctets,但它只在调用GetIfEntry2(&row)后才有效——而row.InterfaceLuid不能直接用网卡名赋值。
必须链式调用:
- 先用
ConvertStringToInterfaceLuid(L"Ethernet", &luid)或GetAdapterIndex转出LUID - 再用
InitializeUnicastIpAddressEntry不是必须的,别混淆 - 调用
GetIfEntry2前确保row.InterfaceLuid已设好,否则返回ERROR_INVALID_PARAMETER - 两次采样间必须sleep至少100ms,否则
OutOctets不变——Windows内核不保证高频更新该计数器
跨平台采样逻辑里最容易被忽略的溢出与重置问题
无论Linux还是Windows,tx_bytes或OutOctets都是无符号64位整数,但网卡驱动偶尔会重置计数器(如热插拔、驱动reload)。如果只做简单减法,会出现巨大负跳变或超大正跳变(比如从0xFFFFFFFFFFFFFFF0跳到0x100)。
检测和应对方法:
- 记录上次值
last_tx,本次读到curr_tx后,检查curr_tx 100_MB(设阈值防误判) - 发现重置就丢弃本次增量,用
curr_tx作为新的基准,不参与速率计算 - 不要用
uint64_t直接存速率值——两次采样间隔若达分钟级,增量可能超int64_t范围,应转成double再除以秒数 - 趋势图横轴用真实采样时间戳(
std::chrono::steady_clock::now()),别用固定间隔推算,否则重置后时间线错位
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











