如何使用Golang检测网络连接是否为按流量计费类型?
检测按流量计费的网络连接(Golang实现)
核心思路
按流量计费的网络标记(Metered Connection)是系统层面的属性,Golang本身没有原生跨平台API,需要通过调用各系统的底层接口或工具来实现。以下分平台给出可行方案:
Linux 平台
Linux下通常由NetworkManager管理网络是否为按流量计费状态,可通过netlink接口或解析系统命令获取:
方法1:使用netlink库(推荐)
借助第三方库github.com/vishvananda/netlink直接获取网络接口的Metered属性:
package main import ( "fmt" "github.com/vishvananda/netlink" ) func main() { links, err := netlink.LinkList() if err != nil { fmt.Printf("获取网络接口失败: %v\n", err) return } for _, link := range links { attrs, err := link.Attrs() if err != nil { continue } // 检查是否为按流量计费 if attrs.Metered == netlink.METERED_YES { fmt.Printf("接口 %s 是按流量计费的\n", attrs.Name) } else if attrs.Metered == netlink.METERED_NO { fmt.Printf("接口 %s 不是按流量计费的\n", attrs.Name) } else { fmt.Printf("接口 %s 的流量计费状态未知\n", attrs.Name) } } }
方法2:执行系统命令(无需第三方库)
通过nmcli命令查询当前活跃连接的Metered状态:
package main import ( "fmt" "os/exec" "strings" ) func main() { cmd := exec.Command("nmcli", "-t", "-f", "NAME,METERED", "connection", "show", "--active") output, err := cmd.Output() if err != nil { fmt.Printf("执行命令失败: %v\n", err) return } lines := strings.Split(string(output), "\n") for _, line := range lines { if line == "" { continue } parts := strings.Split(line, ":") if len(parts) != 2 { continue } connName := parts[0] metered := parts[1] if metered == "yes" { fmt.Printf("连接 %s 是按流量计费的\n", connName) } else { fmt.Printf("连接 %s 不是按流量计费的\n", connName) } } }
Windows 平台
Windows可通过Network List Manager API获取网络连接的IsMetered属性,借助go-ole库实现:
package main import ( "fmt" "github.com/go-ole/go-ole" "github.com/go-ole/go-ole/oleutil" ) func main() { err := ole.CoInitialize(0) if err != nil { fmt.Printf("初始化COM环境失败: %v\n", err) return } defer ole.CoUninitialize() networkListManager, err := oleutil.CreateObject("NetworkListManager") if err != nil { fmt.Printf("创建NetworkListManager对象失败: %v\n", err) return } defer networkListManager.Release() nlmDispatch, err := networkListManager.QueryInterface(ole.IID_IDispatch) if err != nil { fmt.Printf("获取IDispatch接口失败: %v\n", err) return } defer nlmDispatch.Release() networksProp, err := oleutil.GetProperty(nlmDispatch, "Networks") if err != nil { fmt.Printf("获取Networks集合失败: %v\n", err) return } defer networksProp.Clear() networkEnum, err := networksProp.ToIDispatch().QueryInterface(ole.IID_IEnumVARIANT) if err != nil { fmt.Printf("获取枚举器失败: %v\n", err) return } defer networkEnum.Release() var variant ole.VARIANT for { fetched, err := networkEnum.Next(1, &variant, nil) if err != nil || fetched == 0 { break } networkDispatch := variant.ToIDispatch() name, _ := oleutil.GetProperty(networkDispatch, "Name") isMetered, _ := oleutil.GetProperty(networkDispatch, "IsMetered") if isMetered.Value().(bool) { fmt.Printf("网络 %s 是按流量计费的\n", name.Value()) } else { fmt.Printf("网络 %s 不是按流量计费的\n", name.Value()) } variant.Clear() } }
macOS 平台
macOS通过SystemConfiguration框架获取网络配置,使用cgo调用系统API:
package main /* #cgo LDFLAGS: -framework SystemConfiguration #include <SystemConfiguration/SystemConfiguration.h> #include <stdbool.h> bool isNetworkMetered() { bool isMetered = false; SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, "MeteredCheck", NULL, NULL); if (store) { CFPropertyListRef config = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); if (config) { CFDictionaryRef dict = (CFDictionaryRef)config; CFNumberRef type = CFDictionaryGetValue(dict, CFSTR("ConnectionType")); if (type) { int connType; CFNumberGetValue(type, kCFNumberIntType, &connType); // kSCNetworkConnectionTypeMetered 对应值为4 if (connType == 4) { isMetered = true; } } CFRelease(config); } CFRelease(store); } return isMetered; } */ import "C" import "fmt" func main() { if C.isNetworkMetered() { fmt.Println("当前网络是按流量计费的") } else { fmt.Println("当前网络不是按流量计费的") } }
注意事项
- 跨平台项目可通过
runtime.GOOS判断当前系统,分支执行对应平台的代码 - 部分方法需要管理员权限(如Linux下访问netlink接口、Windows下调用COM接口)
- 系统工具(如
nmcli)可能并非所有Linux发行版都预装,需提前确认依赖
内容的提问来源于stack exchange,提问作者OldLearner
相关产品推荐
相关产品推荐

