优先选用Unix Socket(如unix:/tmp/swoole.sock),因其绕过内核网络协议栈、无IP/端口校验、权限可控,实测QPS高15%~20%、延迟低一半;TCP仅用于跨容器或调试场景。

Unix Socket 和 TCP Socket 在 Swoole 里到底怎么选
本地进程间通信,优先用 unix:/tmp/swoole.sock,不是因为“高级”,而是它绕过内核网络协议栈、无 IP/端口校验、文件系统权限可控——实测 QPS 高 15%~20%,延迟低一半。TCP(127.0.0.1:9501)只在需要跨容器、或调试时才用,别被“本地=loopback”误导。
常见错误:启动 Swoole\Http\Server 时写成 new Swoole\Http\Server('unix:/tmp/app.sock', 0) —— 端口号必须为 0,否则 swoole 会报 Invalid port;但 Swoole\Server(TCP 模式)不接受 unix:// 前缀,混用直接崩溃。
-
Swoole\Http\Server和Swoole\WebSocket\Server支持 Unix Socket,构造时 host 传路径、port 传0 -
Swoole\Server(原始 Server)不支持 Unix Socket,强行传会触发Segmentation fault - 路径权限要放开:
chmod 777 /tmp/swoole.sock或用chown www-data:www-data,否则 worker 启动失败报Permission denied
客户端连接 Unix Socket 的三种写法和坑点
PHP 客户端连 Unix Socket,最稳的是 stream_socket_client(),别迷信 curl 或 file_get_contents() —— 它们默认只认 HTTP 协议,对 Unix Socket 路径解析失败,报 Protocol not supported。
正确示例:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
php
$client = stream_socket_client('unix:///tmp/swoole.sock', $errno, $errstr, 3);
if (!$client) {
throw new RuntimeException("connect failed: {$errstr}");
}
fwrite($client, "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n");
echo fread($client, 8192);
fclose($client);
- 路径前缀必须是
unix://(两个斜杠),少一个就变成本地文件读取,报No such file or directory -
curl要加--unix-socket /tmp/swoole.sock参数,且服务端必须返回标准 HTTP 响应头,否则 curl 解析失败 - 用
Swoole\Client时,类型必须设为SWOOLE_SOCK_UNIX_STREAM,不能用SWOOLE_SOCK_TCP
为什么 onConnect 里拿不到客户端真实地址
Unix Socket 没有 IP 地址概念,$fd 对应的 $server->getClientInfo($fd) 返回数组里 remote_ip 是空字符串,remote_port 是 0 —— 这不是 bug,是设计如此。想区分不同客户端,得靠连接时发送的唯一标识(比如 JWT token 或 client_id),或用 getpeername() 提取 socket 文件路径的 inode(不推荐,不稳定)。
- 不要在
onConnect里做 IP 白名单校验,这块逻辑必须前置到客户端身份协商阶段 -
onReceive中首次收到的数据包,建议强制包含client_id字段,服务端存到$server->connections[$fd]里供后续使用 - 如果真要 trace 来源,可在客户端 connect 前用
posix_getpid()打日志,和服务端$fd关联分析
重启服务时 Address already in use 怎么破
Unix Socket 文件不会随进程退出自动删除,旧 /tmp/swoole.sock 残留导致新进程 bind 失败,报 Address already in use。这不是端口占用,是文件锁残留。
- 启动前加清理:
if (file_exists('/tmp/swoole.sock')) { unlink('/tmp/swoole.sock'); } - 更稳妥的做法:在
onStart回调里删,避免多 worker 竞态;同时设置reusable为false,防止多个 master 同时删同一个文件 - 别用
lsof -U | grep swoole查,Unix Socket 不走 netstat/lsof 的 socket 表,得看ls -l /tmp/swoole.sock是否存在
真正麻烦的是热更新场景:旧 worker 还没完全退出,新 master 就试图 bind,此时仅靠 unlink 不够,得配合 max_request 和优雅重启信号控制生命周期。

















