Linux下用getsockopt读取TCP_INFO获取丢包估算值需自行计算tcpi_lost与tcpi_total_retrans比值,但该值仅为连接级累计统计,非实时丢包率,且受SACK机制影响,不反映原始未达丢包。

Linux下用getsockopt读取TCP_INFO获取丢包估算值
Linux内核从3.10起在TCP_INFO结构体中暴露了部分连接质量指标,但要注意:它不直接提供“丢包率”百分比,而是返回累计的tcpi_lost(已标记为丢失的段数)和tcpi_total_retrans(总重传段数),需自行计算比值。实际丢包行为由底层驱动和网络栈共同决定,用户态只能拿到统计快照。
关键点:
-
getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &len)必须在已建立连接的socket上执行,监听socket或未连接的socket会失败 - 不同内核版本字段偏移可能变化,建议用
sizeof(struct tcp_info)校验长度,避免结构体截断 -
tcpi_lost包含被SACK确认后又被标记为丢失的段,不是纯“未到达”的原始丢包,而是TCP认为不可恢复的丢失事件 - 该值是自连接建立以来的累加值,若连接存活时间短、流量少,比值可能无意义
Windows平台没有等价API,GetPerTcpConnectionEStats仅支持部分指标
Windows的GetPerTcpConnectionEStats可获取TCPESTATS结构体,但它不包含lost或retransmits字段。能拿到的最接近的是dwRttVar(RTT方差)和dwDataBytesOut/dwDataBytesIn,但无法推导丢包率。
真实限制:
立即学习“C++免费学习笔记(深入)”;
-
GetPerTcpConnectionEStats需启用TCP_ESTATS_PATH_RW或TCP_ESTATS_SYN_OPTS_RW等能力,普通应用默认无权调用 - 即使成功,返回的
TCPESTATS_PATH中也无丢包计数器;微软文档明确说明“丢包检测由NDIS层完成,不向上层暴露原始计数” - 第三方工具如
netsh interface ipv4 show subinterface显示的“丢包”是ICMP ping统计,与TCP连接无关
跨平台方案只能靠应用层探测,ping或自定义心跳无效
有人尝试用ping目标IP来估算路径丢包,但这完全不可靠:ICMP走独立路径、受防火墙策略影响、不反映TCP MSS分片行为。更糟的是,在TCP长连接中插入周期性ping会干扰拥塞控制逻辑。
可行的替代思路:
- 在应用协议层添加带序列号的心跳包(如每5秒发一个
seq=123,对方回ack=123),统计超时未响应比例——这测的是“业务层可达性”,不是TCP丢包 - 结合
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, ...)和接收缓冲区水位变化,观察recv()返回EAGAIN频率突增,间接提示网络恶化(但无法区分丢包/乱序/延迟) - 使用
libpcap抓包并解析TCP标志位(如重复ACK、SACK块),但需root权限,且实时分析开销大,不适合嵌入生产服务
为什么你看到的“丢包率”工具结果都不准
像ss -i、netstat -s或Prometheus的node_network_receive_errs_total,它们统计层级完全不同:ss -i展示的是tcpi_lost快照,netstat -s是全局TCP协议栈统计(含所有连接),而网卡驱动计数器(如/sys/class/net/eth0/statistics/rx_dropped)反映的是DMA接收失败,三者数值量级可能差几个数量级。
真正要注意的细节:
- Linux的
tcpi_lost只在收到ACK后更新,如果连接空闲、无ACK触发,该值长期不变 - 同一连接多次调用
getsockopt,若tcpi_lost没变,不代表没丢包,可能是重传被后续ACK覆盖了丢失状态 - UDP socket压根没有标准丢包统计接口,
getsockopt(..., IPPROTO_IP, IP_RECVERR, ...)只能捕获ICMP错误报文,且依赖对方主机发送ICMP Port Unreachable


















