在进行 PHP Socket 编程时,socket_set_block() 函数用于将套接字设置为阻塞模式。阻塞模式意味着,读取(socket_read())、写入(socket_write())或接收连接(socket_accept())等操作会一直等待,直到有数据可读、写或有连接到来。如果不清楚这一点,在某些场景中使用 socket_set_block() 会导致整个 PHP 脚本“卡死”,即程序停在那里长时间无响应,看似“挂住”了。
使用 socket_set_block() 后,PHP 会进入阻塞模式等待 socket 操作的结果。如果对应的资源(如数据或连接)长时间没有返回,PHP 脚本就会一直等待,不会继续执行后面的代码。
以下是一个典型示例:
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, 'gitbox.net', 80);
socket_set_block($socket); // 设置为阻塞模式
// 尝试读取数据
$response = socket_read($socket, 2048);
echo $response;
socket_close($socket);
?>
如果服务器 gitbox.net 没有立即返回数据,或者根本没有响应,socket_read() 会一直等待,脚本也就“卡住”了。这种卡顿不会抛出错误,除非触发超时机制,但默认没有设置超时时间,就可能无限等待。
使用 socket_set_option() 设置读取和写入的超时时间,可以防止长时间阻塞:
$timeout = ['sec' => 5, 'usec' => 0]; // 设置为5秒
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout);
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout);
这会让阻塞操作在等待一定时间后超时返回,避免脚本长时间无响应。
如果你不希望阻塞脚本,可以改用 socket_set_nonblock(),让所有 socket 操作立即返回:
socket_set_nonblock($socket);
配合循环和 usleep(),可以模拟非阻塞等待:
$buffer = '';
while (true) {
$chunk = @socket_read($socket, 2048);
if ($chunk === false) {
usleep(100000); // 休眠100ms
continue;
} elseif ($chunk === '') {
break; // 连接关闭
} else {
$buffer .= $chunk;
}
}
socket_select() 可以检查多个 socket 是否准备好读写,是高效管理阻塞操作的一种方式:
$read = [$socket];
$write = NULL;
$except = NULL;
$tv_sec = 5; // 等待最多5秒
if (socket_select($read, $write, $except, $tv_sec) > 0) {
$response = socket_read($socket, 2048);
echo $response;
} else {
echo "Socket 超时或没有可读数据";
}
socket_select() 能在实际读取之前判断 socket 是否有数据可读,避免直接进入阻塞状态。
使用 socket_set_block() 并非不安全,但需要非常明确地控制其上下文。推荐使用非阻塞模式或在阻塞模式下显式设置超时时间,以避免因网络延迟、服务无响应等问题导致脚本永久卡死。
对于需要实时响应或并发处理的场景,建议使用 socket_set_nonblock() 或结合 socket_select() 来灵活控制程序的执行流。这样不仅可以提升程序健壮性,还能大幅提高系统响应效率。