linux下用/proc/net/dev解析出口网卡实时带宽需两次采样tx_bytes(第10列)求差除以时间间隔,结合ip route get 1.1.1.1动态确定出口网卡,跳过前两行表头,匹配网卡名(如"eth0: "),用std::ifstream读取,避免system调用,单位换算为bps时乘8。

Linux下用/proc/net/dev解析出口网卡实时带宽
直接读取/proc/net/dev是开销最低的方式,但注意它只提供累计字节数,需两次采样做差值计算。关键不是“哪个网卡”,而是“哪个是出口”——通常eth0、ens33或wlan0,但必须结合ip route get 1.1.1.1确认默认路由出口,否则可能统计到内网网卡。
实操建议:
- 用
std::ifstream打开/proc/net/dev,跳过前两行表头,逐行std::string::find匹配目标网卡名(注意末尾有空格,如"eth0: ") - 每秒采样一次,取第2列(接收字节)和第10列(发送字节),单位是字节;除以采样间隔秒数再×8得bps
- 别用
system("cat /proc/net/dev")——fork开销大且难解析,纯C++流读取更稳 - 若网卡名含特殊字符(如
docker0),确保字符串匹配时带冒号和空格,避免误匹配docker01
延迟统计必须用ping或ICMP探测,getsockopt(SO_RCVTIMEO)没用
很多人误以为TCP连接能反映网络延迟,其实connect()测的是建连耗时(含SYN重传),不是路径RTT。真实延迟必须走ICMP——要么调用系统ping命令并解析输出,要么用原始套接字发ICMP_ECHO包。前者简单但依赖外部程序,后者可控但需root权限。
实操建议:
- 用
std::system("ping -c 1 -W 1 1.1.1.1 2>/dev/null | grep 'time=' | awk '{print $7}' | cut -d'=' -f2")最省事,但要注意ping返回非零不等于超时(丢包时exit code=1,需检查stdout是否为空) - 若自己构造ICMP包,务必用
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize))加大接收缓冲区,否则高丢包率下会漏回包 - 别用DNS域名测延迟——
getaddrinfo()耗时干扰大,固定用1.1.1.1或8.8.8.8这类IP - 单次ping不准,至少连续发3次取中位数,避免瞬时抖动误判
Windows上替代方案:用GetIfEntry2()而非GetIfTable
GetIfTable已废弃,返回的dwInOctets/dwOutOctets是32位计数器,容易溢出(约4GB后归零),而GetIfEntry2()返回UONGLONG类型,且支持按名称精确查找网卡(InterfaceLuid比适配器描述符可靠)。但延迟仍得靠IcmpSendEcho2发ICMP。
实操建议:
- 先调
ConvertInterfaceAliasToLuid(L"以太网", &luid),再用GetIfEntry2(&luid, &entry)获取实时收发字节,比遍历所有接口快 -
IcmpSendEcho2需要提前IcmpCreateFile(),且每次调用都要传ReplyBuffer指针——别复用同一块内存未清零,否则旧数据干扰 - Windows防火墙默认禁ICMP入站,但不影响本机发出的
echo request,无需额外配置 - 别用WMI查网络性能计数器(如
Win32_PerfFormattedData_Tcpip_NetworkInterface),查询延迟高达200ms+,不适合实时场景
跨平台封装要注意时间精度和采样频率冲突
Linux用clock_gettime(CLOCK_MONOTONIC, &ts),Windows用QueryPerformanceCounter(),两者纳秒级精度看似够用,但实际瓶颈在ICMP往返——如果设成100ms采样间隔,而ping平均RTT是50ms,那半数采样会卡在等待回包,导致带宽计算时间窗口严重偏移。
实操建议:
- 带宽采样和延迟探测必须异步:用独立线程每秒读
/proc/net/dev或GetIfEntry2,另一线程每秒发一次ICMP,结果存std::atomic变量共享 - 延迟探测失败(超时)时,不要填0或-1——下游统计会失真,应保留上次有效值或标记为
std::nullopt - 别把带宽单位硬编码为Mbps,用户可能需要KB/s或Gbps,暴露
bytes_per_second原始值更稳妥 - 容器环境(Docker/K8s)里
/proc/net/dev看到的是宿主机视图,若需容器内网带宽,得进netns或用cgroup v2的net_cls统计,这已超出基础采集范围
真正麻烦的不是读数据,而是定义“出口”——多网卡、策略路由、隧道接口(如wg0)会让出口动态变化,静态配置网卡名迟早出错。得定期重查默认路由,这事没法偷懒。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











