Swoole中host参数显式控制协议栈:0.0.0.0仅IPv4,::仅IPv6(默认支持IPv4映射需bindv6only=0),双栈需addlistener两次;不自动兼容,无内置dual-stack单socket模式。
直接结论:Swoole 启动 TCP/HTTP 服务器时,host 参数决定监听协议栈,IPv4 和 IPv6 不是“同时启用”或“自动兼容”的选项,而是由绑定地址显式控制;默认 0.0.0.0 只监听 IPv4,:: 才监听 IPv6,混用需额外处理。
Swoole 的 Server->addlistener() 或构造函数中 host 字段不支持模糊值(如 "localhost" 或空字符串),也不自动 fallback。它直接传给底层 bind() 系统调用,因此:
host => "0.0.0.0":仅绑定 IPv4 ANY,**不会**接受 IPv6 连接,即使系统开启 IPv6 且双栈开启host => "::":绑定 IPv6 ANY,可接受 IPv4 映射地址(如 ::ffff:192.168.1.100),但前提是内核启用 net.ipv6.bindv6only = 0(默认多数 Linux 发行版为 0)host => "127.0.0.1" 和 host => "::1" 是严格分离的:前者只响应 IPv4 回环,后者只响应 IPv6 回环Swoole 没有内置 “dual-stack” 单 socket 监听模式(即一个 socket 同时处理 IPv4/IPv6)。常见错误是以为设 host => "::" 就能“通吃”,结果发现 IPv4 客户端连不上——尤其在容器或某些云主机上,bindv6only 被设为 1,:: 就真的只收 IPv6。
正确做法是显式添加两个监听器:
$server = new SwooleHttpServer("::", 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP6);$server->addlistener("0.0.0.0", 9501, SWOOLE_SOCK_TCP); // 注意:端口必须相同
SWOOLE_SOCK_TCP6 + "::",处理 IPv6 流量SWOOLE_SOCK_TCP + "0.0.0.0",处理 IPv4 流量port,Swoole 内部会分别管理 fd,但业务逻辑共用同一个 onRequest 回调当使用双监听时,$request->server['remote_addr'] 返回的 IP 字符串格式取决于连接走的是哪个 socket:
"192.168.1.100"
"2001:db8::1" 或完整形式 "2001:0db8:0000:0000:0000:0000:0000:0001"
::ffff:192.168.1.100),Swoole 默认**不自动转换**为 IPv4 格式,remote_addr 就是那个映射地址字符串需要做兼容判断时,别依赖字符串前缀,用 PHP 原生函数更可靠:
if (filter_var($request->server['remote_addr'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { // IPv4} elseif (filter_var($request->server['remote_addr'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { // IPv6}
如果用 ssl 选项启动 HTTPS 服务并监听 "::",客户端通过 IPv6 访问时,TLS 握手仍依赖证书中的 Subject Alternative Name(SAN)。常见坑:
DNS:example.com,没加 IP:2001:db8::1 —— IPv6 直连会触发证书域名不匹配警告换句话说:HTTPS + IPv6 的正确姿势是「用域名访问」,不是「用 IPv6 地址直连」;监听 "::" 只是网络层支持,应用层信任链仍走 DNS。