:連接被拒絕(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獲取握手細節。