windows 用 getifentry2 获取 ifinerrors/ifouterrors 与收发包数差值计算丢包率,需管理员权限、1–5秒间隔采样,并过滤虚拟网卡;linux 通过解析 /proc/net/dev 的 rx_errs/tx_errs 字段同理计算,二者均统计链路层错误而非 ping 丢包。

Windows 上用 GetIfEntry2 获取适配器丢包率(非实时但最可靠)
Windows 没有“实时丢包率”API,所谓丢包率必须靠两次采样差值计算:用 GetIfEntry2 获取 ifInErrors 和 ifOutErrors(输入/输出错误计数),再结合总收发包数推算。注意这不是 ping 丢包率,而是驱动层统计的底层帧错误(CRC 错、缓冲区溢出等)。
关键点:
-
GetIfEntry2需要管理员权限,否则返回ERROR_ACCESS_DENIED - 错误计数是累加值,不能直接当丢包率用;需间隔 1–5 秒调用两次,再用
(inErrors2 - inErrors1) / (inUcastPkts2 - inUcastPkts1)算瞬时比率 - 某些虚拟网卡(如 Hyper-V、WSL2 vEthernet)可能不更新
ifInErrors,实测为 0,需提前过滤掉 - 接口索引(
LUID)比名称更稳定,建议用ConvertInterfaceAliasToLuid从 “以太网” 这类名字转成 LUID 再查
Linux 下读取 /proc/net/dev 并解析 errs 字段
Linux 没有统一 API,最轻量方式是直接解析 /proc/net/dev。每行第二列(rx_bytes)、第三列(rx_packets)、第四列(rx_errs)和第十列(tx_errs)才是关键。丢包率估算公式同上:用两次采样的 rx_errs 差除以 rx_packets 差。
注意细节:
- 第一行是 header,第二行开始才是数据;字段间空格不固定,必须用跳过空白+逐字段读取,不能简单
std::getline+split(" ") - 部分设备(如
lo、docker0)rx_errs永远为 0,纳入计算会导致除零或误导 -
rx_errs包含物理层错误(如 PHY 信号异常),不包含 TCP 重传或 IP 层丢弃——后者得看/proc/net/snmp的TcpRetransSegs - 频繁读
/proc/net/dev开销极低,但别短于 100ms 间隔,内核不会高频更新这些计数器
C++ 跨平台封装时别碰 ICMP 或 ping 实现
有人想用 ICMP 发包测“网络丢包率”,这完全偏离需求:GetIfEntry2 和 /proc/net/dev 统计的是链路层错误,而 ping 测的是端到端三层可达性,受防火墙、路由策略、中间设备限速影响极大,二者数值无对应关系。
真实项目中踩过的坑:
- 用
boost::asio::ip::icmp做“实时丢包监控”,结果在内网千兆环境显示 15% 丢包,实际是对方主机禁 ping,rx_errs为 0 —— 两类指标根本不在一个维度 - 试图把
GetIfEntry2的ifInErrors直接除以系统启动时间算“平均丢包率”,忽略分母应为有效接收包数,导致结果毫无意义 - 没做采样间隔同步:两次
GetIfEntry2调用之间被线程调度打断超 10 秒,导致分母(包增量)过大,丢包率被严重稀释
丢包率低于 0.1% 时多数情况可忽略,但突增需警惕
物理网卡在正常负载下 rx_errs 增长极慢,千兆满载连续跑 24 小时可能只增几个。若观察到 1 秒内 rx_errs 增长 > 100,基本可判定存在硬件问题:网线接触不良、光纤衰减超标、网卡驱动 bug 或交换机端口故障。
工程建议:
- 设置告警阈值别卡死在 1%,推荐动态基线:用过去 5 分钟滑动窗口均值 × 5 倍作为触发线
- 记录原始计数(
rx_errs,rx_packets)而非仅存比率,方便事后回溯是突增还是缓升 - Windows 上若
GetIfEntry2返回ERROR_NO_DATA,大概率是适配器已禁用或驱动未加载,别当成采集失败重试
真正难的不是读数据,而是区分哪些 err 是偶发噪声、哪些预示着下周就要半夜爬起来换光模块。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











