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

时间:2014-03-31 00:03:36

标签: php curl curl-multi

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

以下是一个例子:

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

输出boolean true。因此,换句话说,尽管我调用了curl_close(),但句柄并未关闭。

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

出于绝望,我尝试复制curl_close()行,如下所示:

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

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

那么这里发生了什么?这似乎非常像一个错误!

编辑:我无法发布我的完整代码(你不会想要阅读它!),但这是一个简化的例子(请注意,在这个例子中只提取了一个网址,而在我的真实代码中{ {1}}用于同时获取多个网址:

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()

指示句柄关闭。

4 个答案:

答案 0 :(得分:5)

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

首先,您使用$ch = curl_init();打开句柄并查看ext\curl\interface.c中的来源,您可以看到内部设置ch->uses = 0;

然后您致电curl_multi_add_handle($mh, $ch);并查看ext\curl\multi.c此方法ch->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_handle或使用curl_copy_handle时增加。所以我想这个想法是curl_multi_add_handle使用句柄的副本而不是实际的句柄。

答案 1 :(得分:0)

这没有问题。使用multi-curl时,您无需致电curl_close。相反,您必须在每个使用过的句柄上调用curl_multi_remove_handle。因此,代码中的curl_close调用是多余的。

请在此处查看正确multi-curl流的示例:12

答案 2 :(得分:-1)

“句柄”未在循环中关闭 在循环之后你可以删除句柄

    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]);
    }

答案 3 :(得分:-3)

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

while($chInfo = curl_multi_info_read($mh))

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