2014-03-31 18 views
10

我一直在撞墙撞了几个小时,试图理解为什么cURL的cookie jar文件是空的,当我试着读它时。我刚刚发现,如果我两次调用curl_close()而不是一次,我的代码就可以正常工作,但是,我想知道这是否是一个cURL错误。必须在句柄关闭并且可以读取cookie jar之前调用curl_close()两次。这是一个错误?

下面是一个例子:

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

输出boolean true。换句话说,尽管事实上我叫curl_close(),但手柄并未关闭。

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

出于绝望,我试图复制curl_close()线,像这样:

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

输出boolean false,这意味着关闭句柄,并且我能够从cookie jar文件读取(卷曲写当手柄关闭时,Cookie将文件保存到文件中)。

那么这里发生了什么?这看起来很像一个bug!编辑:我不能发布我的完整代码(你不会想要阅读它!),但这里是一个简化的例子(请注意,在这个例子中只有一个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 

指示句柄是关闭。

+4

这真的很奇怪。我从来没有遇到过这样的问题,而且我经常使用cURL。什么是你的PHP版本?你可以分享cURL执行吗? – sunshinejr

+0

@ailvenge我正在使用PHP版本5.4.12。我为你发布了示例代码。谢谢你的帮助。 – Nate

+2

我很好奇你为什么用curl_close代替curl_multi_close?在使用curl_multi _...的其他地方,我认为使用curl_multi_close也是合乎逻辑的。这只是一个难题.. – bksi

回答

5

这不是真的一个bug,而只是它的方式RKS。如果你看看源代码,你可以看到发生了什么。

起初你打开手柄与$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

现在最后一部分,ext\curl\interface.c看着curl_close($chInfo['handle']);,再次它具有以下代码:

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

非常有趣。感谢您深入研究源代码以找出这种奇怪行为背后的原因!对我来说,cURL的开发者会这么做似乎很奇怪。事实上,必须关闭cURL句柄**才能读取cookie jar /文件,这让我觉得这是一个错误,因为使用multi_curl时,这意味着您有**关闭句柄两次。感谢您接近它! – Nate

+1

那么'curl_multi_add_handle'似乎是'curl_multi_remove_handle',而''是' - 使用'。它看起来像他们期望有人添加要处理的多卷曲的句柄,并且在被删除之后,他们仍然可以访问该句柄。 因此,可以使用'remove; close'来代替'close; close',然后对我来说这似乎完全合乎逻辑。无论如何,感谢代表:) –

-3

我觉得只有1失误寻找到的代码,即

while($chInfo = curl_multi_info_read($mh)) 

变化与

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

这是错误的。 OP的代码是正确的:他将调用的结果分配给一个变量,并检查该变量是否为NULL。如果调用返回非NULL值,则循环继续。 –

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

这是没有问题的。使用multi-curl时,不需要拨打curl_close。相反,您必须在每个使用的手柄上拨打curl_multi_remove_handle。因此,代码中的curl_close调用是多余的。

查看正确的multi-curl流程示例:1,2

+0

根据文档,cookie jar文件不会更新,直到调用'curl_close()'。通过我自己的测试,这似乎是这种情况。正如我的问题所提到的,在cookie jar文件可以被读取之前,我必须调用它两次,所以很明显存在一个问题。 – Nate

+0

@Nate你是否在使用一个cookie jar文件来处理所有curl请求? – hindmost

+0

不,我为每个句柄使用一个单独的文件。 – Nate

相关问题