在使用PHP 編寫基於Socket 的服務器應用時, socket_accept()是接收客戶端連接的關鍵函數。然而在某些場景中,即便程序邏輯正確,也可能遇到socket_accept()無法正常接收連接的情況。本文將介紹如何使用strace和netstat工具,快速定位和解決這一問題。
以下是一個簡單的PHP 服務端程序,監聽8080 端口:
<?php
$host = '0.0.0.0';
$port = 8080;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, $host, $port);
socket_listen($socket);
echo "Server listening on $host:$port\n";
while (true) {
$client = socket_accept($socket);
if ($client === false) {
echo "Failed to accept connection\n";
continue;
}
$msg = "Hello from server\n";
socket_write($client, $msg, strlen($msg));
socket_close($client);
}
?>
當你發現socket_accept()卡住,或者始終返回false時,就需要藉助系統工具來深入排查。
strace是一個Linux 下的系統調用追踪工具,可以直接查看PHP 腳本在運行時調用了哪些系統底層函數,以及它們的執行結果。
找到正在運行的PHP 進程PID,例如使用ps :
ps aux | grep php
使用strace附加到該進程:
strace -p <PID> -e trace=network
觀察輸出中是否存在如下行為:
accept(...) = -1 :系統調用失敗,通常伴隨錯誤碼(如EAGAIN 、 ECONNABORTED等)
listen(...)或bind(...)報錯:可能意味著端口被佔用或權限不足
沒有調用accept() :程序可能卡在其他地方
如果strace顯示accept()正常,但仍然無連接接入,那麼問題可能出在網絡層或防火牆。
netstat和ss可用於檢查端口是否處於監聽狀態,以及是否已有連接建立。
netstat -tlnp | grep :8080
或:
ss -tlnp | grep :8080
你應該能看到類似以下的輸出:
tcp LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("php",pid=12345,fd=3))
如果沒有任何監聽,說明socket_bind()或socket_listen()失敗;
如果監聽正常但無連接,可能是客戶端未發起連接,或被防火牆攔截;
如果狀態為CLOSE_WAIT或TIME_WAIT ,說明連接生命週期異常結束。
檢查錯誤信息<br> 每次Socket 調用後使用socket_strerror(socket_last_error())查看具體錯誤信息
防火牆或SELinux 限制<br> 使用iptables或firewalld確認8080 端口未被阻塞
端口被其他程序佔用<br> 使用lsof -i :8080檢查是否有其他程序佔用了該端口