You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何安全监听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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 17:22:05