解析 /proc/net/dev 需跳过前两行,从第三行起按字段对齐提取数据,接口名后冒号定位,第2和第10个数字字段分别为接收/发送字节数,避免空格分割错误;采样间隔建议1–5秒;日志需行缓冲、微秒时间戳、增量计算防接口增删异常。

读取 /proc/net/dev 需要按字段对齐解析,不能直接 std::getline 拆空格
Linux 的 /proc/net/dev 第一行是固定表头(Inter-| Receive | Transmit),第二行是字段分隔线(全是空格或制表符),真正数据从第三行开始。每行前两个字段是接口名和冒号(如 eth0:),后面 16 个数字字段顺序固定,代表接收/发送的字节数、包数、错误数等。
常见错误是用 std::istringstream 直接按空格切分——因为接口名长度不一(lo: vs enp0s31f6:),字段之间可能有多个空格或制表符,导致字段错位。必须跳过前两行,再对每行用 substr 或正则定位第 12–28 字符后开始找第一个非空位置,才能准确定位字节数字段(第 2 个数字是接收字节数,第 10 个是发送字节数)。
- 只关心总流量?提取第 2 和第 10 个字段即可(索引从 0 开始,跳过接口名和冒号)
- 想区分所有接口?先用
find(':')找冒号位置,再从冒号后第一个非空字符开始解析后续数字 - 别依赖
std::cin >>:它会把连续空格当一个分隔符,破坏字段对齐
std::ifstream 每次打开 /proc/net/dev 都是快照,但需控制采样频率
/proc 是虚拟文件系统,读取 /proc/net/dev 不涉及磁盘 I/O,只是内核拼接当前统计值返回。但频繁读取(比如 100ms 一次)毫无意义——网卡计数器本身更新就不是实时的,且日志写入反而成瓶颈。
- 监控间隔建议设为 1–5 秒:太短看不出趋势,太长漏掉突发流量
- 每次打开后立即读完并关闭,不要
seekg(0)复用流——/proc 文件不支持 seek,重复打开更可靠 - 如果程序运行中网卡热插拔(如 USB 网卡拔出),对应行会消失,解析时需检查行是否含
:且至少有足够字段,避免std::stoull抛异常
写日志时用 std::ofstream 追加模式 + 行缓冲,避免丢数据
流量日志本质是时间序列,每条记录格式通常是 timestamp,interface,rx_bytes,tx_bytes。用 std::ios::app 打开文件最安全,但要注意默认全缓冲——程序崩溃或被 kill 时,最后几条可能滞留在缓冲区没落盘。
- 构造
std::ofstream后立刻调用os.setf(std::ios::unitbuf),强制每行输出后 flush - 别用
std::endl:它既写\n又 flush,效率低;改用\n+os.flush()更可控 - 日志路径别硬编码,比如写死
"./traffic.log";传参或读环境变量,方便容器或 systemd 场景下指定目录 - 注意文件权限:普通用户无法写
/var/log/,默认存/tmp/或$HOME/logs/
两次读取计算差值时,unsigned long long 溢出不是问题,但初始值要处理
网卡计数器是 64 位无符号整数,满值约 1.8×10¹⁹ 字节。即使 10Gbps 满速跑,也要 5000 年才溢出,实际完全不用防溢出。真正容易出错的是第一次读取后没有保存“上一次值”,导致第二次无法计算增量。
- 定义全局或类成员变量
std::map<:string unsigned long> last_rx, last_tx</:string> - 首次读取时只存值,不计算差值;从第二次开始才输出 delta
- 接口在两次读取间消失(如
wlan0断连),查last_rx.count(ifname) == 0就跳过,不强行用 0 做减法 - 时间戳别用
std::time(nullptr):精度只有秒;改用std::chrono::system_clock::now().time_since_epoch().count()得微秒级,方便后续聚合
最难的不是读 /proc,而是保证 delta 计算不跨重启、不因接口增删错乱、不因日志 IO 拖慢主循环。这些细节堆起来,才让脚本从“能跑”变成“敢放生产环境”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











