最可行方式是直接读取/proc/net/dev:每10ms以上间隔读取并计算rx_packets与tx_packets差值,注意跳过头两行、用stoull解析、处理溢出与零值,并避免高频i/o和格式化开销。

Linux 下用 /proc/net/dev 做毫秒级轮询统计最可行
直接读取 /proc/net/dev 是 Linux 上获取网卡收发包频率最轻量、最稳定的方式。它不依赖特权、不引入额外内核模块,且字段含义明确(rx_packets 和 tx_packets 列就是累计包数)。只要每毫秒读一次并做差值,就能得到瞬时速率——但要注意:不是所有系统都允许高频读取,且“毫秒级”在用户态实际受调度和 I/O 延迟影响,通常建议 ≥10ms 间隔更可靠。
常见错误是直接用 std::chrono::high_resolution_clock 控制 sleep 时间却忽略系统调度抖动,导致采样间隔严重漂移。推荐用 clock_nanosleep(CLOCK_MONOTONIC, ...) 或基于事件循环(如 epoll + 定时器)对齐真实时间点。
- 每次读取后跳过前两行(头信息),按空格分割,第2列为接收包数,第10列为发送包数(以
ens33:开头的行为例) - 用
std::stoull()解析,避免atoi溢出(64 位计数器可能超int) - 首次读取只存基准值,第二次起才计算 delta;注意溢出回绕(虽然极罕见,但需判断
new_val )
Windows 上必须用 GetIfEntry2() + GetIfTable2(),别碰 GetIfTable()
GetIfTable() 返回的是 32 位包计数器,现代千兆以上网卡几秒就溢出,完全无法用于毫秒级差分。必须用支持 64 位计数的 GetIfEntry2()(单网卡)或 GetIfTable2()(全表),它们返回 MIB_IF_ROW2,其中 ReceivedUnicastPackets 和 TransmittedUnicastPackets 字段才是可靠的。
容易踩的坑是调用前没初始化 WSAStartup(),或忽略 GetIfTable2() 的内存所有权——它分配的内存必须用 FreeMibTable() 释放,否则每秒泄漏几 KB。
- 接口索引(
InterfaceIndex)比适配器名称更稳定,建议先用ConvertInterfaceAliasToLuid()将 “Ethernet” 映射为 LUID,再转索引 - 两次调用间隔不能短于 10ms,否则
GetIfEntry2()可能返回上一周期缓存值,造成 delta = 0 - 需要
#pragma comment(lib, "iphlpapi.lib"),且最低支持 Windows 8 / Server 2012
跨平台封装时,别抽象“获取包数”接口,而要暴露“采样间隔容忍度”
很多跨平台库试图统一写个 get_packet_rate(),结果在 Linux 上跑得飞快,在 Windows 上卡死或返回零。根本原因是底层机制差异太大:Linux 的 /proc 是无锁文本读取,Windows 的 GetIfEntry2() 是内核态查表+用户态拷贝,还带内部缓存策略。
本文档主要讲述的是Android 操作系统的介绍;Android是基于Linux内核的操作系统,是Google公司在2007年11月5日公布的手机操作系统,早期由Google开发,后由开放手持设备联盟(Open Handset Alliance)开发。它采用了软件堆层(software stack,又名以软件叠层)的架构,主要分为三部分。底层Linux内核只提供基本功能;其他的应用软件则由各公司自行开发,部分程序以Java编写。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
真正可维护的做法是让调用方明确声明时间精度需求。例如:
struct PacketSampler {
virtual ~PacketSampler() = default;
// 返回 true 表示本次采样有效(delta 已更新)
virtual bool sample(std::uint64_t* rx_delta, std::uint64_t* tx_delta) = 0;
// 告知调用方:本实现能稳定支持的最小间隔(单位 ms)
virtual int min_stable_interval_ms() const = 0;
};
Linux 实现返回 1,Windows 实现返回 10——上层逻辑据此决定是否合并多次采样或降频。
实时性 ≠ 高频,丢包检测和环形缓冲区比“毫秒”更重要
真正做网络监控时,用户要的不是“每毫秒一个数字”,而是“能否及时发现突发丢包或流量尖刺”。硬塞毫秒级输出反而会因日志刷屏或 UI 更新拖慢主线程。更合理的做法是:
- 后台线程以 10ms 间隔采样,把
(timestamp, rx_delta, tx_delta)写入无锁环形缓冲区(如boost::lockfree::spsc_queue) - 主逻辑每 100ms 拉取一批数据,计算滑动窗口内的 P95 包率、突增倍数(当前值 / 均值 > 3x?)、连续零值次数(链路中断信号)
- 避免用
std::cout或printf直接打每毫秒数据——格式化开销远超采集本身
最常被忽略的一点:网卡驱动本身的统计延迟。某些 Realtek 或旧 Intel 卡的 rx_packets 计数器实际是中断聚合后批量更新的,即使你读得再快,底层硬件也没更新。此时看 /sys/class/net/eth0/statistics/ 下的原始寄存器值(需 root)反而更准,但已超出通用 C++ 范畴。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










