在PHP 中,使用原生socket 編程時, socket_accept()函數用於接受客戶端的連接請求,但默認情況下,這個函數是阻塞式的——也就是說,如果沒有客戶端連接,程序會一直等待下去,無法自動超時。為了避免程序因等待連接而卡死或無響應,我們可以結合socket_set_timeout()來實現連接超時控制,從而提升程序的健壯性。
本文將詳細介紹如何利用socket_accept()和socket_set_timeout()來實現PHP 連接超時控制的功能。
socket_accept($socket)
阻塞地等待客戶端連接請求,如果有連接請求則返回新的socket 資源,否則一直阻塞。
socket_set_timeout($socket, $seconds, $microseconds)
為指定的socket 設置超時時間。超時後,讀取/寫入操作會返回超時錯誤。
需要注意的是, socket_set_timeout()是針對讀寫操作的超時控制,並不能直接控制socket_accept()的阻塞等待時間。因此,我們需要配合其他方式(例如非阻塞模式或使用socket_select() )來實現真正的連接等待超時。
設置監聽socket 為非阻塞模式<br> 這樣調用socket_accept()不會阻塞,如果沒有連接請求,會立即返回fals e
使用循環+延時配合超時判斷<br> 在循環裡反複調用socket_accept( ) ,如果返回fals e ,判斷是否超時,超時則退出等待
一旦接收到連接,使用socket_set_timeout()設置讀寫超時<br> 保證後續數據傳輸過程中有超時控制
<?php
$host = "0.0.0.0";
$port = 12345;
$timeoutSeconds = 10; // 最大等待連接時間
// 創建 TCP socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
die("socket_create failed: " . socket_strerror(socket_last_error()) . "\n");
}
// 綁定端口
if (socket_bind($socket, $host, $port) === false) {
die("socket_bind failed: " . socket_strerror(socket_last_error($socket)) . "\n");
}
// 監聽端口
if (socket_listen($socket, 5) === false) {
die("socket_listen failed: " . socket_strerror(socket_last_error($socket)) . "\n");
}
// 設置監聽 socket 為非阻塞
socket_set_nonblock($socket);
$startTime = time();
$clientSocket = false;
echo "等待客戶端連接,超時時間:{$timeoutSeconds}秒\n";
while (true) {
$clientSocket = @socket_accept($socket);
if ($clientSocket !== false) {
echo "客戶端已連接!\n";
break;
}
// 判斷是否超時
if ((time() - $startTime) >= $timeoutSeconds) {
echo "等待客戶端連接超时。\n";
break;
}
// 休眠 100ms 避免 CPU 佔用過高
usleep(100000);
}
if ($clientSocket !== false) {
// 設置客戶端 socket 讀寫超時,例如 5 秒
socket_set_option($clientSocket, SOL_SOCKET, SO_RCVTIMEO, ["sec"=>5, "usec"=>0]);
socket_set_option($clientSocket, SOL_SOCKET, SO_SNDTIMEO, ["sec"=>5, "usec"=>0]);
// 讀取客戶端數據示例
$buf = '';
$bytes = socket_recv($clientSocket, $buf, 2048, 0);
if ($bytes === false) {
echo "接收數據失敗或超時:" . socket_strerror(socket_last_error($clientSocket)) . "\n";
} else {
echo "收到客戶端數據:" . $buf . "\n";
}
socket_close($clientSocket);
}
socket_close($socket);
?>
非阻塞監聽:通過socket_set_nonblock() ,避免socket_accept()阻塞,允許我們自定義超時邏輯。
循環等待連接:每次調用socket_accept() ,如果無連接立即返回false ,判斷是否達到超時條件,若未超時則繼續等待。
連接後設置讀寫超時:使用socket_set_option()給客戶端socket 設置讀寫超時,防止後續讀取數據時永久阻塞。
usleep() 減少CPU 佔用:避免空循環導致CPU 佔用過高。
通過將監聽socket 設置為非阻塞,並結合循環輪詢判斷時間,實現了對socket_accept()阻塞等待連接的超時控制。同時,在接收連接後利用socket_set_option()設置讀寫超時,保證數據傳輸過程的穩定性和安全性。
這套方案簡單實用,非常適合需要手工管理socket 連接超時的PHP 服務端程序。