net.ListenUnix 和 net.DialUnix 不处理路径合法性等约束,而是由 Linux 系统强制要求:路径必须绝对且长度≤108字节,否则 bind(2) 失败;需用 filepath.Abs 转换并检查字节长度。

net.ListenUnix 和 net.DialUnix 本身不处理路径合法性、生命周期或消息边界——这些不是语言特性问题,而是 Unix 域套接字(UDS)在 Linux 系统上的硬性约束。Go 语言只是暴露了系统调用接口,真正决定成败的是你是否按操作系统规则操作。
路径必须绝对且≤108字节,否则直接 bind: invalid argument
Linux 内核对 sun_path 字段有严格长度限制(108 字节),且只接受绝对路径。相对路径如 "./sock" 或空字符串会立刻触发错误,不是 Go 报错,是 bind(2) 系统调用失败。
- 启动前必须用
filepath.Abs(sockPath)转成绝对路径,别依赖当前工作目录 - 检查长度:
len([]byte(sockPath)) ,含路径中所有字符(包括 <code>/) - 禁止空格、
\0、非 UTF-8 字节;建议只用 ASCII 字母、数字、_、-、. - 优先选
/run/myapp.sock(systemd 场景),避开/tmp(可能被PrivateTmp=true隔离)
服务端启动前不清理残留文件,必然 address already in use
address already in use 在 UDS 场景下不是端口冲突,而是磁盘上已有同名 socket 文件。Go 的 listener.Close() 不删文件,下次启动就会撞上。
- 每次启动服务前执行
os.RemoveAll(sockPath),忽略os.IsNotExist错误 - 监听前验证父目录存在且可写:
os.Stat(filepath.Dir(sockPath))+ 尝试写临时文件测试 - 别在
defer里只写os.Remove—— 进程崩溃时不会执行,残留文件仍会卡住下次启动
客户端连不上?90% 是服务端根本没起来,不是路径写错
dial unix /tmp/mysock: connect: no such file or directory 这个错误只有一个含义:服务端压根没成功调用 net.ListenUnix。它不是客户端配置问题,而是服务端启动失败的间接信号。
立即学习“go语言免费学习笔记(深入)”;
- 别用
net.Dial("unix", path),显式构造&net.UnixAddr{Net: "unix", Name: path},避免底层地址类型歧义 - 客户端和服务端的
Name字符串必须字节级完全一致——"/tmp/sock"和"/tmp/sock/"是两个不同路径 - 加简单重试逻辑:
for i := 0; i - 用
ls -l /path/to/your.sock手动确认类型是s(socket),不是普通文件或目录
流式通信不加协议,必然粘包;bufio.Writer 不 Flush(),消息就丢
UDS 的 SOCK_STREAM 和 TCP 一样是字节流,conn.Read() 不保证读完一条完整消息,bufio.Writer 默认缓冲,不 Flush() 就 close,数据永远发不出去。
- 最简可靠方案:每条消息前加 4 字节大端长度头,
binary.Write(conn, binary.BigEndian, uint32(len(data))) - 接收方先
io.ReadFull(conn, header[:4]),再按长度读正文,别用conn.Read(buf)直接读满缓冲区 - 每个
bufio.Writer写完后必须调w.Flush(),否则数据卡在内存里 - 设读写超时:
conn.SetReadDeadline(time.Now().Add(5 * time.Second)),防异常断连后阻塞
真正的难点不在代码怎么写,而在你是否把 UDS 当作一个“带权限的特殊文件”来管理——路径是文件路径,生命周期要手动维护,通信边界要自己定义。系统不会替你做任何容错,Go 也不会。


















