PHP 的socket 默認是阻塞模式,這意味著在嘗試讀或寫之前,如果沒有數據或資源準備好,調用就會一直等待。而非阻塞模式下,調用會立即返回。
我們使用socket_set_block($socket)將套接字設置為阻塞模式,隨後結合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);
?>
阻塞行為:由於使用了socket_set_block , socket_accept將會在沒有連接請求時阻塞等待。結合socket_select後,可以避免線程空轉。
靈活控制超時: socket_select的超時參數可以用來控制阻塞時間,這在一些需要輪詢或心跳機制的服務中非常有用。
多客戶端支持:通過將多個套接字添加到$read數組中, socket_select能有效支持多個連接,便於構建高並發服務端。