bigywater.tech
Rust · 143

Unix Domain Socket

同一台机器上的进程通信,Unix 域套接字比 TCP 回环好在哪?它的地址是文件,带来哪些坑?这篇验证套接字文件、残留文件错误、路径长度限制、socketpair,并把延迟和吞吐与 TCP 回环对比。

4.7 对 17.2 µs
Unix 域与 TCP 回环的 4 字节往返延迟
103 字节
本机上可 bind 的最长套接字路径(再长一字节失败)
48 / 61
残留套接字文件时 bind 与 connect 的 OS 错误码

套接字是文件

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    bind 之后路径上出现了一个文件:is_socket = true,权限 755
      服务端:对端地址 None(客户端没有 bind,是未命名的)
    客户端:收到 "你好 uds"
    listener 已经 drop,但套接字文件还在: true(需要自己删除)

TCP 版本见TCP Listener 与 TcpStream;文件描述符的释放见文件描述符与资源释放。

残留的套接字文件

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    上一个 listener 已经退出,文件还在: true
    再 bind 同一个路径:Err((AddrInUse, Some(48)))
    connect 到这个没人监听的残留文件:Err((ConnectionRefused, Some(61)))
    删除文件后 bind: Ok("成功")
    connect 到不存在的路径:Err((NotFound, Some(2)))

路径长度

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    目录部分 53 字节;文件名最长 49 字节时 bind 成功,总路径长度 103 字节;文件名 50 字节(总长 104)失败:Err("path must be shorter than SUN_LEN")

macOS 的 sun_path 是 104 字节,Linux 是 108 字节,这里只验证了 macOS。

socketpair 与数据报

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    UnixStream::pair:a 写 ping,b 读到 "ping";a 的地址 None
    UnixDatagram:发 3 字节与 2 字节,两次 recv 分别得到 3 与 2 字节(边界保留)

与 TCP 回环对比

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    往返延迟:Unix 域套接字 4.7 µs,TCP 回环 17.2 µs
    单向吞吐(64 KiB 块,1 GiB):Unix 域套接字 1.76 GB/s,TCP 回环 3.94 GB/s

吞吐与延迟是 macOS 上的单次微基准,不能推广到 Linux。

分步演示

Unix 域套接字
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。macOS;文件放在系统临时目录
    • 没有验证:Linux 的抽象命名空间套接字(@name)、通过 SCM_RIGHTS 传递文件描述符、SO_PEERCRED / getpeereid 取对端身份(std 里 peer_cred 不稳定)、套接字文件的权限控制访问、tokio 的 UnixStream
    京ICP备2026067502号-1京公网安备11010502063487号