Linux下无需轮询获取NetworkManager连接状态变更通知的Go实现方案
监听Linux NetworkManager网络连接状态变更的Go实现方案
要替代定时轮询,你可以利用Linux下的DBus事件机制(NetworkManager官方推荐方式),或者通过inotify监听NetworkManager状态文件实现事件驱动触发,以下是具体方案:
方案一:通过DBus监听NetworkManager信号
NetworkManager通过系统DBus总线发布状态变更信号,这是最可靠、信息最丰富的事件驱动方式,类似inotify的工作模式。
实现步骤
- 安装Go的DBus客户端库:
go get github.com/godbus/dbus/v5
- 示例代码(监听全局网络状态变更):
package main import ( "fmt" "os" "github.com/godbus/dbus/v5" ) func main() { // 连接系统DBus总线 conn, err := dbus.SystemBus() if err != nil { fmt.Fprintln(os.Stderr, "无法连接系统DBus:", err) os.Exit(1) } defer conn.Close() // 订阅NetworkManager的StateChanged信号 matchRule := "type='signal',interface='org.freedesktop.NetworkManager',member='StateChanged'" if call := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, matchRule); call.Err != nil { fmt.Fprintln(os.Stderr, "添加信号匹配失败:", call.Err) os.Exit(1) } // 接收信号的通道 sigChan := make(chan *dbus.Signal, 10) conn.Signal(sigChan) fmt.Println("正在监听网络状态变更...") for sig := range sigChan { if sig.Name == "org.freedesktop.NetworkManager.StateChanged" { newState := sig.Body[0].(uint32) stateDesc := parseNMState(newState) fmt.Printf("网络状态更新: %s (状态码: %d)\n", stateDesc, newState) // 在这里触发你的耗时函数 // yourHeavyFunction() } } } // 解析NetworkManager状态码为可读描述 func parseNMState(state uint32) string { switch state { case 0: return "NM_STATE_UNKNOWN" case 10: return "NM_STATE_ASLEEP" case 20: return "NM_STATE_DISCONNECTED" case 30: return "NM_STATE_DISCONNECTING" case 40: return "NM_STATE_CONNECTING" case 50: return "NM_STATE_CONNECTED_LOCAL" case 60: return "NM_STATE_CONNECTED_SITE" case 70: return "NM_STATE_CONNECTED_GLOBAL" default: return "未知状态" } }
若需监听具体连接的激活/停用事件,可修改匹配规则和信号逻辑,订阅org.freedesktop.NetworkManager.ConnectionActivated或org.freedesktop.NetworkManager.ConnectionDeactivated信号,这类信号会携带具体连接的详细信息。
方案二:用inotify监听NetworkManager状态文件
如果偏好轻量实现,可监听NetworkManager的状态文件/run/NetworkManager/state,网络状态变化时该文件内容会同步更新。
实现步骤
- 安装Go的inotify库:
go get github.com/fsnotify/fsnotify
- 示例代码:
package main import ( "fmt" "os" "strings" "github.com/fsnotify/fsnotify" ) func main() { watcher, err := fsnotify.NewWatcher() if err != nil { fmt.Fprintln(os.Stderr, "创建inotify监听器失败:", err) os.Exit(1) } defer watcher.Close() stateFilePath := "/run/NetworkManager/state" if err := watcher.Add(stateFilePath); err != nil { fmt.Fprintln(os.Stderr, "无法监听状态文件:", err) os.Exit(1) } fmt.Println("正在监听网络状态文件变更...") for { select { case event, ok := <-watcher.Events: if !ok { return } // 文件写入或重新创建时触发 if event.Has(fsnotify.Write) || event.Has(fsnotify.Remove) { content, err := os.ReadFile(stateFilePath) if err != nil { fmt.Fprintln(os.Stderr, "读取状态文件失败:", err) continue } for _, line := range strings.Split(string(content), "\n") { if strings.HasPrefix(line, "STATE=") { stateStr := strings.TrimPrefix(line, "STATE=") fmt.Printf("网络状态更新: %s\n", stateStr) // 触发你的耗时函数 // yourHeavyFunction() } } } case err, ok := <-watcher.Errors: if !ok { return } fmt.Fprintln(os.Stderr, "监听器错误:", err) } } }
方案对比
- DBus方式:官方标准,支持获取详细事件(如具体连接信息),实时性强,适合精准触发场景。
- inotify文件监听:实现简单,无需了解DBus协议,但仅能获取全局状态,信息有限,且文件更新可能存在轻微延迟。
内容的提问来源于stack exchange,提问作者Amirography
相关产品推荐
相关产品推荐

