socket_set_block 函数的主要功能是将一个 socket 设置为阻塞模式。阻塞模式意味着,当你执行诸如 socket_read 或 socket_write 这类操作时,如果数据尚未准备好,程序将暂停等待,直到数据可用或操作完成。
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, "gitbox.net", 80);
socket_set_block($socket);
socket_write($socket, "GET / HTTP/1.1\r\nHost: gitbox.net\r\nConnection: Close\r\n\r\n");
$response = '';
while ($out = socket_read($socket, 2048)) {
$response .= $out;
}
socket_close($socket);
echo $response;
?>
在上述代码中,socket_set_block 确保了 socket_read 会等待数据的到来,不会立即返回。
阻塞模式的优点
当程序在阻塞调用时,CPU 会被释放给操作系统执行其他任务。换句话说,CPU 不会忙于轮询 socket 状态,而是进入休眠,等待操作系统通知数据可用。这使得 CPU 使用率较低,资源得到合理利用。
阻塞模式的缺点
阻塞意味着程序会停在某个调用点,如果网络或对端响应缓慢,程序也会被“卡住”,无法处理其他任务。在高并发场景下,如果使用阻塞模式的多个 socket,可能导致应用响应缓慢。
虽然阻塞模式天然有助于降低 CPU 占用,但在实际应用中仍可能遇到 CPU 占用异常高的问题。常见原因包括:
频繁非阻塞轮询(busy waiting)
当开发者使用非阻塞模式或自己实现轮询逻辑时,程序会不停调用读取函数,造成 CPU 一直处于忙碌状态。
错误的阻塞/非阻塞模式切换
如果 socket 状态切换频繁且未合理控制,可能导致系统调用效率低下,增加 CPU 负载。
缺少事件驱动或异步机制
仅用阻塞方式处理大量 socket,导致程序无法并行处理多个连接。
socket_select 可以监听多个 socket 状态,当其中一个或多个 socket 可读可写时才进行操作,避免忙等。
<?php
$read = [$socket];
$write = $except = null;
if (socket_select($read, $write, $except, 5) > 0) {
foreach ($read as $readableSocket) {
$data = socket_read($readableSocket, 2048);
// 处理数据
}
}
?>
这样,程序只有在 socket 有数据时才会被唤醒,减少 CPU 空转。
阻塞模式下,可通过 socket_set_option 设置超时,防止长时间阻塞:
<?php
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ["sec"=>3, "usec"=>0]);
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, ["sec"=>3, "usec"=>0]);
?>
适当调节缓冲区大小也可以减少系统调用次数和上下文切换。
对于高并发应用,可通过多进程或多线程来分担阻塞带来的瓶颈,避免单进程阻塞影响整体性能。
PHP 有诸如 Swoole、ReactPHP 等异步扩展,支持非阻塞和事件驱动模型,极大提高资源利用率,降低 CPU 占用。
socket_set_block 让 socket 操作阻塞,减少 CPU 空转,提高单连接的效率。
阻塞模式在高并发场景下可能导致应用响应缓慢。
结合 socket_select 等多路复用技术,合理设置超时参数,可以显著降低 CPU 占用。
采用异步或多进程模型是提升性能和资源利用的有效手段。
理解 socket_set_block 对 CPU 影响的原理,并配合合理优化,能让 PHP 网络应用更加高效稳定。