PHP 大会日本 2024

curl_multi_select

(PHP 5, PHP 7, PHP 8)

curl_multi_select等待任何 cURL 多句柄连接的读取或写入操作变为可能

描述

curl_multi_select(CurlMultiHandle $multi_handle, float $timeout = 1.0): int

阻止脚本执行,直到附加到 cURL 多句柄的 cURL 句柄能够在下次调用 curl_multi_exec() 时取得进展,或者直到超时(以先发生者为准)。

参数

multi_handle

curl_multi_init() 返回的 cURL 多句柄。

timeout

等待来自活动 cURL 多句柄连接的响应的时间(以秒为单位)。

返回值

成功时,返回描述符集中包含的活动描述符的数量。如果任何描述符上都没有活动,则这可能为 0。失败时,如果选择失败(来自底层的 select() 系统调用),则此函数将返回 -1

错误/异常

如果 timeout 小于 0 或大于 PHP_INT_MAX,则抛出 ValueError

变更日志

版本 描述
8.4.0 现在如果 timeout 小于 0 或大于 PHP_INT_MAX,则抛出 ValueError
8.0.0 multi_handle 现在期望一个 CurlMultiHandle 实例;以前,期望的是 resource

参见

添加注释

用户贡献的注释 8 条注释

41
nevil85 at gmail dot com
10 年前
即使经过了这么多次发行版,在 PHP v5.5.9 和 libcurl v7.35.0 中,curl_multi_select 仍然返回 -1。这里唯一的解决方法是等待并继续,就像什么也没发生一样。您必须在此处确定所需的“等待”时间。

在我的应用程序中,一个非常小的间隔,比如 usleep(1),可以工作。例如

<?php
// 当我们仍然处于活动状态时,执行 curl
$active = null;
do {
$mrc = curl_multi_exec($multi, $active);
} while (
$mrc == CURLM_CALL_MULTI_PERFORM);

while (
$active && $mrc == CURLM_OK) {
// 等待任何 curl 连接上的活动
if (curl_multi_select($multi) == -1) {
usleep(1);
}

// 继续执行,直到 curl 准备好
// 向我们提供更多数据
do {
$mrc = curl_multi_exec($multi, $active);
} while (
$mrc == CURLM_CALL_MULTI_PERFORM);
}
?>

在内部,php curl_multi_select 使用 libcurl curl_multi_fdset 函数,其 libcurl 文档说
http://curl.haxx.se/libcurl/c/curl_multi_fdset.html

"当 libcurl 在 max_fd 中返回 -1 时,这是因为 libcurl 当前正在执行某些操作,您的应用程序无法使用套接字监视这些操作,并且不幸的是,您无法准确知道当前操作何时使用 select() 完成。当 max_fd 返回 -1 时,您需要等待一段时间,然后继续并无论如何调用 curl_multi_perform。等待多长时间?我建议至少 100 毫秒,但您可能需要在自己的特定条件下对其进行测试以找到合适的值。

在执行 select() 时,您应该使用 curl_multi_timeout 来确定等待操作多长时间。"

在 PHP 实现 curl_multi_timeout() 之前,我们必须使用我们的应用程序并确定“等待”时间。
3
bishop at php dot net
5 年前
在 PHP 7.1.23 和 7.2.11 之前,如果 cURL 没有打开的文件描述符,则 curl_multi_select() 将返回 -1。从那时起,假设 libcurl 7.28 或更高版本,如果 cURL 没有打开的文件描述符,则 curl_multi_select() 将返回 0,如果出错则返回 -1。

参见 https://bugs.php.net/bug.php?id=76480

这是一个 BC 中断。参见 https://bugs.php.net/bug.php?id=77030
16
vigo dot von dot harrach at gmx dot de
12 年前
curl_multi_select($mh, $timeout) 只是在 curl_multi_exec() 返回 CURLM_CALL_MULTI_PERFORM 时阻塞 $timeout 秒。否则,它按预期工作,并阻塞直到至少一个连接完成或 $timeout 秒,以先发生者为准。

因此,curl_multi_exec() 应该始终被包装

<?php
function full_curl_multi_exec($mh, &$still_running) {
do {
$rv = curl_multi_exec($mh, $still_running);
} while (
$rv == CURLM_CALL_MULTI_PERFORM);
return
$rv;
}
?>

这样,“多”处理的核心就变成了(为了简洁起见,忽略错误处理)

<?php
full_curl_multi_exec
($mh, $still_running); // 启动请求
do { // "等待完成"循环
curl_multi_select($mh); // 非繁忙 (!) 等待状态改变
full_curl_multi_exec($mh, $still_running); // 获取新状态
while ($info = curl_multi_info_read($mh)) {
// 处理已完成的请求(例如 curl_multi_getcontent($info['handle']))
}
} while (
$still_running);
?>

请注意,在启动请求后,检索是在后台完成的——这是 PHP 中并行处理的最佳方法之一。
10
grik dot net 上的 public
16 年前
此函数会阻塞调用进程,直到 curl_multi 接口打开的任何连接上有活动,或者直到超时时间已过期。
换句话说,它会等待在打开的连接中接收数据。

在内部,它使用 "curl_multi_fdset()" 获取套接字指针,并运行 "select()" C 函数。
它在 3 种情况下返回
1. 在任何套接字上检测到活动;
2. 超时已结束(第二个参数);
3. 进程接收到任何信号(#man kill)。

该函数返回一个整数
* 如果检测到活动,则返回一个数字,通常为 1。
我假设,它返回检测到活动的连接数。
* 如果超时过期,则返回 0
* 如果发生错误,则返回 -1

感谢您的关注,希望这能有所帮助。
4
xxavalanchexx at gmail dot com
10 年前
根据https://bugs.php.net/bug.php?id=61141:

在使用 libcurl 版本 7.24 或更高版本(似乎对应于 PHP 5.3.10 或更高版本)的 Windows 设置上,您可能会发现它始终返回 -1。这显然不是严格意义上的错误:根据 libcurl 文档,如果 curl_multi_select 返回 -1,您应该添加自己的 sleep。

例如
<?php
/* 设置 $mh */

$active = null;
do {
$mrc = curl_multi_exec($mh, $active);
} while (
$mrc == CURLM_CALL_MULTI_PERFORM);

while (
$active && $mrc == CURLM_OK) {
if (
curl_multi_select($mh) == -1) {
usleep(100);
}
do {
$mrc = curl_multi_exec($mh, $active);
} while (
$mrc == CURLM_CALL_MULTI_PERFORM);
}

/* 关闭 $mh */
?>
6
匿名用户
11 年前
在 5.3.9+ 中,curl_multi_select 始终返回 -1。如果这是您的情况,只需等待片刻,然后继续执行,就像什么也没发生一样。
<?php
do {
$mrc = curl_multi_exec($multi, $active);
} while (
$mrc == CURLM_CALL_MULTI_PERFORM);

while (
$active && $mrc == CURLM_OK) {
// 检查结果并执行直到所有操作完成

if (curl_multi_select($multi) == -1) {
// 如果它返回 -1,等待片刻,但无论如何继续执行!
usleep(100);
}

// 对返回值执行某些操作
while(($info = curl_multi_info_read($multi)) !== false){
if (
$info["result"] == CURLE_OK){
$content = curl_multi_getcontent($info["handle"]);
do_something($content);
}
}
do {
$mrc = curl_multi_exec($multi, $active);
} while (
$mrc == CURLM_CALL_MULTI_PERFORM);
}
?>
1
gib-o-master
3 年前
注意:`curl_multi_select` 的当前实现不会阻塞,也不会遵守超时参数(可能以后会修复,在这种情况下只需删除 usleep 调用)

在这里,我想发布我关于此函数的正确用法的工作/测试示例

```
$running = 1;
while ($running)
{
# 执行请求
if ($a = curl_multi_exec($this->murl, $running)) {
throw BotError::text("curl_multi_exec[$a]: ".curl_multi_strerror($a));
}
# 检查已完成
if (!$running) {
break;
}
# 等待活动
while (!$a)
{
if (($a = curl_multi_select($this->murl, $timeout)) < 0)
{
throw BotError::text(
($a = curl_multi_errno($this->murl))
? "curl_multi_select[$a]: ".curl_multi_strerror($a)
: '系统 select 失败'
);
}
usleep($timeout * 1000000);# 应小于等于 1
}
}
```
6
Alex Palmer
11 年前
在 php 5.3.18+ 上请注意,curl_multi_select() 可能会永远返回 -1,直到您调用 curl_multi_exec()。
有关更多信息,请参阅https://bugs.php.net/bug.php?id=63411
To Top