在句柄关闭之前必须调用 curl_close() 两次,并且可以读取 cookie jar。这是一个错误吗?

Have to call curl_close() twice before handle is closed and cookie jar can be read. Is this a bug?

提问人:Nate 提问时间:3/31/2014 最后编辑:Nate 更新时间:4/11/2014 访问量:1800

问:

几个小时来,我一直在用头撞墙,试图理解为什么当我尝试阅读 cURL 的 cookie jar 文件时它是空的。但是,我刚刚发现如果我调用两次而不是一次,我的代码就可以工作,我想知道这是否是 cURL 的错误。curl_close()

下面是一个示例:

curl_close($chInfo['handle']);
var_dump(is_resource($chInfo['handle']));

输出 .所以,换句话说,句柄没有关闭,尽管我调用了.boolean truecurl_close()

我的下一个想法是,也许关闭手柄需要一些时间,所以我在通话后尝试使用几秒钟,但没有任何区别。sleep()curl_close()

出于无奈,我试着复制了这一行,像这样:curl_close()

curl_close($chInfo['handle']);
curl_close($chInfo['handle']);
var_dump(is_resource($chInfo['handle']));

输出 ,这意味着句柄已关闭,并且我能够从 cookie jar 文件中读取(当句柄关闭时,cURL 将 cookie 写入文件)。boolean false

那么这是怎么回事呢?这似乎很像一个错误!

编辑:我无法发布我的完整代码(无论如何你都不想阅读它!),但这是一个简化的例子(请注意,在这个例子中只获取了一个 URL,而在我的实际代码中用于同时获取多个 URL):curl_multi

$curlOptions = array(
    CURLOPT_USERAGENT      => 'Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101',
    CURLOPT_CONNECTTIMEOUT => 5, // the number of seconds to wait while trying to connect.
    CURLOPT_TIMEOUT        => 5, // the maximum number of seconds to allow cURL functions to execute.
    CURLOPT_RETURNTRANSFER => 1, // TRUE to return the transfer as a string of the return value of curl_exec() instead of outputting it out directly.
    CURLOPT_FOLLOWLOCATION => 1,
    CURLOPT_MAXREDIRS      => 10,
    CURLOPT_AUTOREFERER    => 1,
    CURLOPT_REFERER        => null,
    CURLOPT_POST           => 0,  // GET request by default
    CURLOPT_POSTFIELDS     => '', // no POST data by default
    CURLINFO_HEADER_OUT    => 1, // allows the request header to be retrieved
    CURLOPT_HEADER         => 1, // returns the response header along with the page body
    CURLOPT_URL            => 'http://www.example.com/',
    CURLOPT_COOKIEJAR      => __DIR__ . '/cookie.txt',
    CURLOPT_COOKIEFILE     => __DIR__ . '/cookie.txt'
);


$ch = curl_init();
curl_setopt_array($ch, $curlOptions); // set the options for this handle

$mh = curl_multi_init();
$responses = array();
curl_multi_add_handle($mh, $ch); // add the handle to the curl_multi object

do
{
    $result   = curl_multi_exec($mh, $running);
    $activity = curl_multi_select($mh);    // blocks until there's activity on the curl_multi connection (in which case it returns a number > 0), or until 1 sec has passed

    while($chInfo = curl_multi_info_read($mh))
    {
        $chStatus = curl_getinfo($chInfo['handle']);

        if($chStatus['http_code'] == 200) // if the page was retrieved successfully
        {
            $response = curl_multi_getcontent($chInfo['handle']); // get the response

            curl_multi_remove_handle($mh, $chInfo['handle']); // remove the curl handle that was just completed
            curl_close($chInfo['handle']);                    // close the curl handle that was just completed (cookies are saved when the handle is closed?)
            curl_close($chInfo['handle']);

            var_dump(is_resource($chInfo['handle']));
        }
        else // request failed
        {
            echo 'Error: Request failed with http_code: ' . $chStatus['http_code'] . ', curl error: ' . curl_error($chInfo['handle']). PHP_EOL;
        }
    }
} while ($running > 0);

curl_multi_close($mh);

如果运行上述代码,输出将为

boolean false

表示手柄已关闭。但是,如果删除对 的第二次调用,则输出将更改为curl_close()

boolean true

指示手柄关闭。

php curl-multi

评论

4赞 sunshinejr 3/31/2014
这真的很奇怪。我从来没有遇到过这样的问题,我经常使用 cURL。你的PHP版本是什么?您可以共享 cURL 执行吗?
2赞 bksi 4/7/2014
我很好奇你为什么用curl_close而不是curl_multi_close?在你使用curl_multi_的其他地方,我认为使用curl_multi_close也是合乎逻辑的。这只是一个tougth..
1赞 Nate 4/7/2014
@bksi我用于在成功检索页面后关闭句柄。我在所有手柄都完成处理后使用。示例代码只获取一个 URL,但我的真实代码使用 .curl_close()curl_multi_close()curl_multi
1赞 bksi 4/7/2014
我看到的这部分代码向我展示了您使用相同的$ch来执行请求。我不确定你为什么这样使用它。以下是使用 curl_multi_add_handle 的简单示例: se2.php.net/manual/en/function.curl-multi-close.php 他们使用不同的 curl 实例来添加处理程序。
1赞 Nate 4/7/2014
@bksi 在所有 cURL 请求都完成处理之前,我无法调用。一旦单个 cURL 句柄完成处理,我就会调用它们,然后在所有句柄完成处理后调用它们。curl_multi_close()curl_multi_remove_handle()curl_close()curl_multi_close()

答:

-3赞 Vineet1982 4/7/2014 #1

我认为在查看代码后只有 1 个错误,即

while($chInfo = curl_multi_info_read($mh))

更改为

while($chInfo == curl_multi_info_read($mh))

评论

2赞 Aleks G 4/7/2014
这是错误的。OP 的代码是正确的:他将调用的结果分配给一个变量,并检查该变量是否为 NULL。如果调用返回非 NULL 值,则循环将继续。
-1赞 Baine Sumpin 4/8/2014 #2

“句柄”在循环中未闭合 循环后,您可以移除手柄

    curl_multi_remove_handle($mh, $ch1);
    /* this is not suppose to be required but the remove sometimes fails to close the connection */
    curl_close($ch1); 
    curl_multi_remove_handle($mh, $ch2);
    curl_close($ch2);

if you set up your connections as an array you can remove them through a separate loop after the main loop.

    /* init and add connection */
    foreach ($multi_urls as $i => $url) 
    {
        $ch[$i] = curl_init($url);
        curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1);
        curl_multi_add_handle ($mh, $ch[$i]);
    }

    main loop {
        ....
    }

    /* remove and close connection */
    foreach($ch AS $i => $conn)
    { 
       curl_multi_remove_handle($mh, $ch[$i]);
       curl_close($ch[$i]);
    }
5赞 Hugo Delsing 4/11/2014 #3

这不是一个真正的错误,而只是它的工作方式。如果你看一下源代码,你可以看到发生了什么。

首先,您打开手柄并查看源代码,您可以看到它在内部设置$ch = curl_init();ext\curl\interface.cch->uses = 0;

然后你调用并查看此方法确实.在这一点上curl_multi_add_handle($mh, $ch);ext\curl\multi.cch->uses++;ch->uses==1

现在最后一部分,再看一下,其中有如下代码:curl_close($chInfo['handle']);ext\curl\interface.c

if (ch->uses) {
    ch->uses--;
} else {
    zend_list_delete(Z_LVAL_P(zid));
}

因此,第一次尝试关闭它会减少,第二次尝试实际上会关闭它。ch->uses

此内部指针仅在使用 或使用 时增加。所以我想这个想法是为了使用句柄的副本,而不是实际的句柄。curl_multi_add_handlecurl_copy_handlecurl_multi_add_handle

评论

1赞 Nate 4/14/2014
很有意思。感谢您深入研究源代码以找出这种奇怪行为背后的原因!在我看来,cURL 的开发人员会这样做似乎很奇怪。必须关闭 cURL 句柄才能读取 cookie jar/文件这一事实让我觉得这是一个错误,因为在使用 multi_curl 时,这意味着您必须关闭句柄两次。感谢您深入了解它!
1赞 Hugo Delsing 4/14/2014
好吧,似乎相反,确实如此.看起来他们希望有人将手柄添加到要处理的多卷曲中,并且在将其删除后,他们仍然可以访问手柄。所以代替你也可以使用,然后这对我来说似乎是完全合乎逻辑的。无论如何,感谢您的代表:)curl_multi_add_handlecurl_multi_remove_handle--ch->usesclose;close;remove;close
0赞 hindmost 4/11/2014 #4

这里没有问题。使用时无需调用 .相反,您必须调用每个使用的句柄。因此,代码中的调用是多余的。multi-curlcurl_closecurl_multi_remove_handlecurl_close

请参阅此处的正确流程示例:12multi-curl

评论

0赞 Nate 4/11/2014
根据文档,cookie jar 文件在被调用之前不会更新。通过我自己的测试,情况似乎是这样。正如我的问题中提到的,在读取 cookie jar 文件之前,我必须调用它两次,所以显然存在问题。curl_close()
0赞 hindmost 4/11/2014
@Nate 您是否对所有 curl 请求使用一个 cookie jar 文件?
0赞 Nate 4/12/2014
不,我为每个句柄使用单独的文件。
0赞 hindmost 4/14/2014
我曾多次合作过,但从未遇到过保存/加载 cookie 的任何问题。请在此处查看我的实现示例(对不起,插入):github.com/hindmost/rolling-curl-minimulti-curlmulti-curl