如何安全监听Unix域套接字?避免地址已占用问题
检测Unix域套接字是否被进程监听的方法
要判断Unix域套接字是否正被监听,避免误删正在使用的套接字文件,可以通过以下两种可靠方式实现:
方法一:尝试连接套接字(纯Go跨平台实现)
直接发起短连接到目标套接字,若连接成功则说明套接字正被监听;若返回connection refused错误,说明套接字文件存在但无进程监听,可安全删除。
示例代码:
package main import ( "context" "fmt" "net" "os" ) func isUdsListening(ctx context.Context, udsPath string) bool { conn, err := net.DialContext(ctx, "unix", udsPath) if err == nil { conn.Close() return true } // 过滤"连接拒绝"错误,此时套接字无监听 if opErr, ok := err.(*net.OpError); ok { if sysErr, ok := opErr.Err.(*os.SyscallError); ok && sysErr.Err.Error() == "connection refused" { return false } } // 文件不存在等其他情况,均视为未被监听 return false } func main() { ctx := context.Background() udsName := "dummy.socket" // 检查套接字文件状态 if _, err := os.Stat(udsName); err == nil { if !isUdsListening(ctx, udsName) { if err := os.Remove(udsName); err != nil { panic(fmt.Sprintf("删除闲置套接字失败: %v", err)) } fmt.Println("已删除闲置套接字文件") } else { panic(fmt.Sprintf("套接字 %s 已被其他进程占用", udsName)) } } // 启动监听 var lc net.ListenConfig _, err := lc.Listen(ctx, "unix", udsName) if err != nil { panic(fmt.Sprintf("Unix域套接字监听失败: %v", err)) } fmt.Println("服务启动正常") }
方法二:借助系统工具lsof(类Unix系统专用)
通过调用lsof工具查询是否有进程持有该套接字的监听描述符,这种方式依赖系统工具,跨平台性稍差,但结果直观。
示例代码:
package main import ( "context" "fmt" "net" "os" "os/exec" "strings" ) func isUdsInUse(udsPath string) bool { // 调用lsof查询监听该套接字的进程 cmd := exec.Command("lsof", "-F", "p", "-a", "-U", "-d", "LISTEN", "-n", "-P", udsPath) output, err := cmd.Output() if err != nil { // 无匹配进程时lsof会返回非0,直接视为未被占用 return false } // 有输出则说明存在监听进程 return strings.TrimSpace(string(output)) != "" } func main() { ctx := context.Background() udsName := "dummy.socket" if _, err := os.Stat(udsName); err == nil { if !isUdsInUse(udsName) { if err := os.Remove(udsName); err != nil { panic(fmt.Sprintf("删除套接字失败: %v", err)) } fmt.Println("已删除闲置套接字") } else { panic(fmt.Sprintf("套接字 %s 正被其他进程监听", udsName)) } } var lc net.ListenConfig _, err := lc.Listen(ctx, "unix", udsName) if err != nil { panic(fmt.Sprintf("监听失败: %v", err)) } fmt.Println("服务启动正常") }
注意事项
- 方法一是优先选择的纯Go方案,无需依赖外部工具,跨平台性好。
- 两种方法都存在极小的竞态窗口:检测完成后到删除套接字前,可能有其他进程绑定该套接字。可结合
Listen的错误处理,当遇到address already in use时再次验证。
内容的提问来源于stack exchange,提问作者guettli
相关产品推荐
相关产品推荐

