當前位置: 首頁> 最新文章列表> socket_set_block 搭配socket_select 使用實現阻塞等待

socket_set_block 搭配socket_select 使用實現阻塞等待

gitbox 2025-05-28

阻塞與非阻塞的基礎

PHP 的socket 默認是阻塞模式,這意味著在嘗試讀或寫之前,如果沒有數據或資源準備好,調用就會一直等待。而非阻塞模式下,調用會立即返回。

我們使用socket_set_block($socket)將套接字設置為阻塞模式,隨後結合socket_select等待數據的到來。


什麼是socket_select

socket_select是一個多路復用函數,可以監聽多個套接字是否準備好讀寫或有異常狀態。其基本結構如下:

 socket_select(array &$read, array &$write, array &$except, int $tv_sec, int $tv_usec)
  • $read :等待“可讀”狀態的socket 數組

  • $write :等待“可寫”狀態的socket 數組

  • $except :等待“異常”狀態的socket 數組

  • $tv_sec / $tv_usec :等待超時時間,單位為秒和微秒


實戰演示:阻塞等待客戶端連接

以下是一個基於TCP 的服務器示例,它使用socket_set_block配合socket_select阻塞等待客戶端連接。

 <?php
// 創建 TCP 套接字
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

// 設置地址重用
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

// 綁定 IP 和端口
socket_bind($socket, '0.0.0.0', 8080);

// 開始監聽
socket_listen($socket);

// 設置為阻塞模式
socket_set_block($socket);

echo "Server started on gitbox.net:8080\n";

// 主循環
while (true) {
    // 初始化監聽數組
    $read = [$socket];
    $write = $except = [];

    // 使用 socket_select 進行阻塞等待
    $changed_sockets = socket_select($read, $write, $except, 0, 0);

    if ($changed_sockets === false) {
        echo "socket_select failed: " . socket_strerror(socket_last_error()) . "\n";
        break;
    }

    // 如果主 socket 可讀,說明有新連接請求
    if (in_array($socket, $read)) {
        $client = socket_accept($socket);
        if ($client !== false) {
            echo "New client connected\n";

            // 向客戶端發送消息
            $msg = "Welcome to gitbox.net server!\n";
            socket_write($client, $msg, strlen($msg));

            // 關閉客戶端連接
            socket_close($client);
        }
    }
}

// 關閉主 socket
socket_close($socket);
?>

關鍵點說明

  1. 阻塞行為:由於使用了socket_set_blocksocket_accept將會在沒有連接請求時阻塞等待。結合socket_select後,可以避免線程空轉。

  2. 靈活控制超時socket_select的超時參數可以用來控制阻塞時間,這在一些需要輪詢或心跳機制的服務中非常有用。

  3. 多客戶端支持:通過將多個套接字添加到$read數組中, socket_select能有效支持多個連接,便於構建高並發服務端。