Windows无现成API直接返回丢包率,需用GetIfTable2两次采样计算ifInErrors+ifInDiscards与ifInUcastPkts的比率,注意防回绕、除零及丢包定义边界。

Windows 上没有现成的 API 直接返回「丢包率」这个值——它不是网卡硬件直接上报的指标,而是必须通过周期性采样 GetIfEntry2 或 GetIfTable2 获取收发包计数,再自己计算差值与比率得出的推导值。
为什么不能直接读「丢包率」字段?
几乎所有 Windows 网络接口的 MIB-II / WMI / ETW 数据源中,都不存在一个叫 PacketLossRate 或类似名称的实时寄存器。所谓「丢包」发生在多个层级(驱动接收队列溢出、TCP 层重传超时、ICMP echo timeout 等),系统不统一归因,也不聚合为单一百分比。
实际能拿到的是底层可累加计数器:
-
ifInUcastPkts/ifOutUcastPkts:单播收/发包总数 -
ifInErrors/ifOutErrors:接收/发送错误帧数(含 CRC 错、缓冲区溢出等) -
ifInDiscards/ifOutDiscards:因资源不足被静默丢弃的包(如内存不够、队列满)
其中 ifInErrors + ifInDiscards 才接近「入向丢包总数」,但注意:它不包含上层协议重传导致的逻辑丢包(如 TCP 重传、UDP 无反馈丢包)。
立即学习“C++免费学习笔记(深入)”;
用 GetIfTable2 做两次采样并计算差值
这是最轻量、权限要求最低、且支持 IPv6 和多适配器的方案。关键不是“一次调用”,而是“两次调用+时间差+防回绕”:
- 第一次调用
GetIfTable2,记录每个PMIB_IF_ROW2的InUcastPkts、InErrors、InDiscards和OutUcastPkts、OutErrors、OutDiscards - 间隔 1–5 秒后再次调用,获取新值
- 对每个字段做无符号 64 位整数差值(需处理
ULONGLONG回绕,即新值 new + (2^64 - old) 计算) - 丢包率 =
(InErrors + InDiscards) * 100.0 / (InUcastPkts + InErrors + InDiscards)(入向);同理可算出向
示例片段(简化):
// 第二次采样后 ULONGLONG in_total = row2.InUcastPkts + row2.InErrors + row2.InDiscards; ULONGLONG in_dropped = row2.InErrors + row2.InDiscards; double loss_rate = (in_total > 0) ? (in_dropped * 100.0 / in_total) : 0.0;
注意:如果 in_total 为 0(比如网卡刚启用、无流量),此时除零要 guard,且丢包率无意义,应跳过或标记为 N/A。
和 Performance Counter(PDH)对比的坑
有人会想到用 PdhAddCounter 读 \Network Interface(*)\Packets Received Discarded 这类计数器,但它有三个硬伤:
- 计数器名依赖本地化语言(英文系统是
Packets Received Discarded,中文系统是收到丢弃的数据包),不跨语言稳定 - 某些虚拟网卡(如 Hyper-V vSwitch、WSL2 虚拟 NIC)不暴露完整 PDH 计数器,返回
PDH_INVALID_DATA - PDH 初始化开销大,每秒轮询易触发
PDH_CSTATUS_INVALID_HANDLE,不如GetIfTable2直接可靠
所以除非你明确只监控物理以太网卡且系统语言固定,否则别走 PDH 路线。
Linux 下怎么搞?(补充说明)
Linux 没有统一 API,但可通过读取 /sys/class/net/<iface>/statistics/</iface> 下文件获得原始计数:
-
/sys/class/net/eth0/statistics/rx_errors、rx_dropped、rx_packets -
/sys/class/net/eth0/statistics/tx_errors、tx_dropped、tx_packets
同样需两次读取、计算 delta、防回绕(虽然 64 位 unsigned long long 在内核里一般不会溢出,但仍建议检查是否递减)。注意:某些 driver 不实现全部字段(如 rx_dropped 可能永远为 0),此时只能依赖 rx_errors 和 rx_missed_errors 等组合估算。
真正的难点从来不是“怎么拿到数字”,而是定义清楚你要监控的丢包类型——是驱动层丢弃?协议栈丢弃?还是应用层感知到的端到端不可达?不同目标,采集点和解释方式完全不同。别一上来就写“丢包率=99%”,先确认分母是谁、分子漏了哪几类丢弃路径。


















