當前位置: 首頁> 最新文章列表> socket_accept() 配合socket_set_timeout 實現連接超時控制

socket_accept() 配合socket_set_timeout 實現連接超時控制

M66 2025-05-28

在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() )來實現真正的連接等待超時。

二、實現思路

  1. 設置監聽socket 為非阻塞模式<br> 這樣調用socket_accept()不會阻塞,如果沒有連接請求,會立即返回fals e

  2. 使用循環+延時配合超時判斷<br> 在循環裡反複調用socket_accept( ) ,如果返回fals e ,判斷是否超時,超時則退出等待

  3. 一旦接收到連接,使用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 服務端程序。