:连接被拒绝(Connection refused)或长时间超时;端口不可达。
原因:本机或对端防火墙(iptables、ufw、云厂商安全组)阻止端口,或中间 NAT/ACL 阻断。
排查与解决:
在本服务器上用 telnet host port / nc -vz host port / curl --connect-timeout 测试端口连通性。
检查云平台安全组、宿主机 iptables、主机防火墙日志。
开放或添加允许规则(只开放必要 IP/端口),或调整安全组策略。
若是被 ISP/机房限制,联系网络管理员或换出口。
现象:socket_connect 卡很久后失败,或脚本被阻塞。
原因:默认阻塞行为且未设置合理超时;远端无响应或响应慢。
排查与解决:
使用非阻塞连接并自行实现超时检查,或使用 stream_socket_client 并指定超时参数。
PHP sockets 示例(设置超时):
<span><span><span class="hljs-variable">$sock</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_create</span></span><span>(AF_INET, SOCK_STREAM, SOL_TCP);
</span><span><span class="hljs-title function_ invoke__">socket_set_nonblock</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>);
@</span><span><span class="hljs-title function_ invoke__">socket_connect</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>, </span><span><span class="hljs-variable">$host</span></span><span>, </span><span><span class="hljs-variable">$port</span></span><span>);
</span><span><span class="hljs-variable">$start</span></span><span> = </span><span><span class="hljs-title function_ invoke__">time</span></span><span>();
</span><span><span class="hljs-variable">$timeout</span></span><span> = </span><span><span class="hljs-number">5</span></span><span>;
</span><span><span class="hljs-keyword">while</span></span><span> (!@</span><span><span class="hljs-title function_ invoke__">socket_connect</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>, </span><span><span class="hljs-variable">$host</span></span><span>, </span><span><span class="hljs-variable">$port</span></span><span>)) {
</span><span><span class="hljs-variable">$err</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_last_error</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$err</span></span><span> === SOCKET_EISCONN) </span><span><span class="hljs-keyword">break</span></span><span>;
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">time</span></span><span>() - </span><span><span class="hljs-variable">$start</span></span><span> > </span><span><span class="hljs-variable">$timeout</span></span><span>) {
</span><span><span class="hljs-title function_ invoke__">socket_close</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>);
</span><span><span class="hljs-keyword">throw</span></span><span> </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-built_in">Exception</span></span><span>(</span><span><span class="hljs-string">"connect timeout"</span></span><span>);
}
</span><span><span class="hljs-title function_ invoke__">usleep</span></span><span>(</span><span><span class="hljs-number">100000</span></span><span>);
}
</span><span><span class="hljs-title function_ invoke__">socket_set_block</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>);
</span></span>
更简单的:stream_socket_client("tcp://host:port", $errno, $errstr, $timeout)。
现象:脚本无法建立外连,日志无明显网络错误。
原因:SELinux 或 AppArmor 对进程网络访问有限制;或 PHP-FPM 的 open_basedir / disable_functions 限制。
排查与解决:
查看 /var/log/audit/audit.log(SELinux)或 system log,检查是否有拒绝条目。
临时以 permissive 模式测试,或为服务添加合适的 SELinux 策略。
确认 PHP 配置没有禁用网络相关函数(如 socket_create 被禁用)。
现象:连接成功但没响应或数据异常。
原因:目标服务使用 UDP,但客户端用 TCP,或反之;协议层面(比如期望 TLS 但直接 TCP)。
排查与解决:
确认服务协议(HTTP/HTTPS、TLS、SMTP、Redis、MySQL 等)与连接方式一致。
如果是 TLS/SSL,需要使用 stream_socket_client('ssl://host:port', ...) 或在 sockets 上使用 OpenSSL 扩展函数 stream_socket_enable_crypto()。
现象:主机既有 IPv4 又有 IPv6,连接到错误地址族导致失败或超时。
解决:
明确使用 AF_INET(IPv4)或 AF_INET6(IPv6)。
使用 getaddrinfo 或 dns_get_record() 得到具体地址,再选择合适族。
现象:连接建立但握手失败,出现 OpenSSL 错误。
排查与解决:
使用 stream_socket_client('ssl://host:port', $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $context) 并配置 ssl 上下文(cafile、verify_peer、SNI)。示例:
<span><span><span class="hljs-variable">$context</span></span><span> = </span><span><span class="hljs-title function_ invoke__">stream_context_create</span></span><span>([
</span><span><span class="hljs-string">'ssl'</span></span><span> => [
</span><span><span class="hljs-string">'verify_peer'</span></span><span> => </span><span><span class="hljs-literal">true</span></span><span>,
</span><span><span class="hljs-string">'cafile'</span></span><span> => </span><span><span class="hljs-string">'/etc/ssl/certs/ca-certificates.crt'</span></span><span>,
</span><span><span class="hljs-string">'SNI_enabled'</span></span><span> => </span><span><span class="hljs-literal">true</span></span><span>,
</span><span><span class="hljs-string">'peer_name'</span></span><span> => </span><span><span class="hljs-string">'example.com'</span></span><span>,
]
]);
</span><span><span class="hljs-variable">$fp</span></span><span> = </span><span><span class="hljs-title function_ invoke__">stream_socket_client</span></span><span>(</span><span><span class="hljs-string">"ssl://example.com:443"</span></span><span>, </span><span><span class="hljs-variable">$errno</span></span><span>, </span><span><span class="hljs-variable">$errstr</span></span><span>, </span><span><span class="hljs-number">5</span></span><span>, STREAM_CLIENT_CONNECT, </span><span><span class="hljs-variable">$context</span></span><span>);
</span></span>
查看 OpenSSL 版本与服务器支持的密码套件,必要时升级或调整套件。
检查证书链是否完整(中间证书漏装会导致握手失败)。
现象:高并发下连接失败、报“Too many open files”。
原因:系统 ulimit -n 或进程最大 open-files 限制。
排查与解决:
用 ulimit -n 查看当前限制;调整 systemd/服务配置或 /etc/security/limits.conf 提高限制。
在程序中复用连接(连接池)、使用异步/事件驱动模型减少并发 socket 数量。
现象:仅知道“连接失败”,但不知道原因。
解决:
使用 socket_last_error() 与 socket_strerror() 获取详细错误信息:
<span><span><span class="hljs-variable">$err</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_last_error</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>);
</span><span><span class="hljs-variable">$msg</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_strerror</span></span><span>(</span><span><span class="hljs-variable">$err</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">error_log</span></span><span>(</span><span><span class="hljs-string">"socket error: <span class="hljs-subst">$err</span></span></span><span> - </span><span><span class="hljs-subst">$msg</span></span><span>");
</span></span>
捕获并记录上下文(目标 IP、端口、时间、超时时间),结合系统日志与 tcpdump 分析。
现象:连接到外网但行为异常(请求被拦截、替换、重写)。
排查与解决:
确认网络路径中是否有代理(企业网、ISP)或 WAF。
使用 tcpdump / wireshark 抓包,查看 TCP 三次握手与后续包是否被修改或重置(RST)。
如有代理,按代理要求添加认证或走代理通道。
原生 sockets 功能强大但编写更复杂。对于常见 TCP / HTTP / SSL 场景,优先考虑:
stream_socket_client():内置超时、可直接支持 ssl://,更易用。
cURL(libcurl)扩展:适用于 HTTP/HTTPS,有丰富选项与超时设置。
专用客户端库(Redis、MySQL、AMQP 等)通常处理了很多细节。
示例:用 stream_socket_client 简洁地连接并读写:
<span><span><span class="hljs-variable">$timeout</span></span><span> = </span><span><span class="hljs-number">5</span></span><span>;
</span><span><span class="hljs-variable">$fp</span></span><span> = </span><span><span class="hljs-title function_ invoke__">stream_socket_client</span></span><span>(</span><span><span class="hljs-string">"tcp://example.com:12345"</span></span><span>, </span><span><span class="hljs-variable">$errno</span></span><span>, </span><span><span class="hljs-variable">$errstr</span></span><span>, </span><span><span class="hljs-variable">$timeout</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-variable">$fp</span></span><span>) {
</span><span><span class="hljs-title function_ invoke__">error_log</span></span><span>(</span><span><span class="hljs-string">"connect failed: <span class="hljs-subst">$errno</span></span></span><span> </span><span><span class="hljs-subst">$errstr</span></span><span>");
} </span><span><span class="hljs-keyword">else</span></span><span> {
</span><span><span class="hljs-title function_ invoke__">stream_set_timeout</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>, </span><span><span class="hljs-number">5</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">fwrite</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>, </span><span><span class="hljs-string">"hello\n"</span></span><span>);
</span><span><span class="hljs-variable">$resp</span></span><span> = </span><span><span class="hljs-title function_ invoke__">fgets</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">fclose</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>);
}
</span></span>
本地先 ping / dig / telnet / nc 验证基础连通性。
在 PHP 中打印并记录 socket_last_error 与 socket_strerror。
使用抓包(tcpdump)确认三次握手是否成功、是否有 RST/ICMP 错误。
检查服务端日志(若能访问)看是否收到连接请求或拒绝。
检查服务器防火墙(iptables/nftables、云安全组)、SELinux 策略与 PHP-FPM 权限。
尝试在同机或同网段的另一台主机上连接以排除网络出口问题。
若是 TLS 问题,测试 openssl s_client -connect host:port -servername host 获取握手细节。