当前位置: 首页> 最新文章列表> 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 能有效支持多个连接,便于构建高并发服务端。