当服务器拒绝连接时处理每个句柄时,句柄将包含错误消息.
问题
我假设当我们得到一个坏句柄时,CURL将标记此句柄但继续处理未处理的句柄,但这似乎不会发生.
当我们遇到错误的句柄时,CURL会将此句柄标记为错误,但不会处理剩余的未处理句柄.
这可能很难检测,如果我确实得到了所有句柄的连接,这是大多数时间发生的事情,那么问题就不可见了.(CURL只在第一次错误连接时停止);
对于测试,我必须找到一个合适的站点,加载缓慢/拒绝x量同时连接.
set_time_limit(0); $l = array( 'http://smotri.com/video/list/','http://smotri.com/video/list/sports/','http://smotri.com/video/list/animals/','http://smotri.com/video/list/travel/','http://smotri.com/video/list/hobby/','http://smotri.com/video/list/gaming/','http://smotri.com/video/list/mult/','http://smotri.com/video/list/erotic/','http://smotri.com/video/list/auto/','http://smotri.com/video/list/humour/','http://smotri.com/video/list/film/' ); $mh = curl_multi_init(); $s = 0; $f = 10; while($s <= $f) { $ch = curl_init(); $curlsettings = array( CURLOPT_URL => $l[$s],CURLOPT_TIMEOUT => 0,CURLOPT_CONNECTTIMEOUT => 0,CURLOPT_RETURNTRANSFER => 1 ); curl_setopt_array($ch,$curlsettings); curl_multi_add_handle($mh,$ch); $s++; } $active = null; do { curl_multi_exec($mh,$active); curl_multi_select($mh); $info = curl_multi_info_read($mh); echo '<pre>'; var_dump($info); if($info['result'] === CURLE_OK) echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' success<br>'; if($info['result'] != 0) echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' Failed<br>'; } while ($active > 0); curl_multi_close($mh);
我在脚本中抛出了$info,询问Multi Handle是否在运行时有任何句柄的新信息.
当脚本结束时,我们将看到一些bool(false) – 当没有新信息可用时(句柄仍处理),以及所有句柄如果一切成功或有限句柄,如果一个句柄失败.
我没有解决这个问题,这可能是我忽略的事情,而且我试图修复那些不相关的东西,我已经走得太远了.
一些解决这个问题的尝试是.
>将每个$ch句柄分配给一个数组 – $ch [1],$ch [2]等(而不是
将当前$ch句柄添加到multi_handle然后覆盖 – 如何
在测试中)
>成功/失败后删除句柄
curl_ MULTI_ remove_处理
>将CURLOPT_CONNECTTIMEOUT和CURLOPT_TIMEOUT设置为无穷大.
>还有更多.(我会更新这篇文章,因为我已经忘记了所有这些)
使用PHP版本5.4.14进行测试
希望我已经很好地说明了这些观点.
谢谢阅读.
解决方法
额外的while循环使其表现完全符合您的预期.这是我得到的输出:
http://smotri.com/video/list/sports/ Failed http://smotri.com/video/list/travel/ Failed http://smotri.com/video/list/gaming/ Failed http://smotri.com/video/list/erotic/ Failed http://smotri.com/video/list/humour/ Failed http://smotri.com/video/list/animals/ success http://smotri.com/video/list/film/ success http://smotri.com/video/list/auto/ success http://smotri.com/video/list/ Failed http://smotri.com/video/list/hobby/ Failed http://smotri.com/video/list/mult/ Failed
这是我用于测试的代码:
<?PHP set_time_limit(0); $l = array( 'http://smotri.com/video/list/','http://smotri.com/video/list/film/' ); $mh = curl_multi_init(); $s = 0; $f = 10; while($s <= $f) { $ch = curl_init(); if($s%2) { $curlsettings = array( CURLOPT_URL => $l[$s],CURLOPT_TIMEOUT_MS => 3000,CURLOPT_RETURNTRANSFER => 1,); } else { $curlsettings = array( CURLOPT_URL => $l[$s],CURLOPT_TIMEOUT_MS => 4000,); } curl_setopt_array($ch,$ch); $s++; } $active = null; do { $mrc = curl_multi_exec($mh,$active); curl_multi_select($mh); while($info = curl_multi_info_read($mh)) { echo '<pre>'; //var_dump($info); if($info['result'] === 0) { echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' success<br>'; } else { echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' Failed<br>'; } } } while ($active > 0); curl_multi_close($mh);
希望有所帮助.要进行测试,只需将CURLOPT_TIMEOUT_MS调整为您的互联网连接即可.我做到了这样它在3000到4000毫秒之间交替,因为3000将失败并且4000通常成功.
更新
在浏览了PHP和libCurl文档之后,我发现curl_multi_exec是如何工作的(在libCurl中它的curl_multi_perform).在第一次被调用时,它开始处理所有添加的句柄的传输(在通过curl_multi_add_handle之前添加).
它指定$活动的数字是仍在运行的转移数量.因此,如果它小于您拥有的句柄总数,那么您就知道一个或多个传输已完成.所以curl_multi_exec也是一种进度指标.
由于所有传输都以非阻塞方式处理(传输可以同时完成),因此while循环curl_multi_exec不能代表已完成的url请求的每次迭代.
所有数据都存储在队列中,因此只要完成一个或多个传输,您就可以调用curl_multi_info_read来获取此数据.
在我的原始答案中,我在while循环中使用了curl_multi_info_read.此循环将继续迭代,直到curl_multi_info_read在队列中找不到剩余数据.如果$active!= 0(意味着curl_multi_exec报告传输仍未完成),则外部while循环将移动到下一次迭代.
总而言之,当仍然没有完成传输时,外部循环继续迭代,并且内部循环仅在完成传输的数据时才进行迭代.
PHP文档对于curl多功能非常糟糕,所以我希望这清除了一些东西.下面是另一种做同样事情的方法.
do { curl_multi_exec($mh,$active); } while ($active > 0); // while($info = curl_multi_info_read($mh)) would work also here for($i = 0; $i <= $f; $i++){ $info = curl_multi_info_read($mh); if($info['result'] === 0) { echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' success<br>'; } else { echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' Failed<br>'; } }
根据这些信息,您还可以看到不需要curl_multi_select,因为您不希望在有活动之前阻塞某些东西.
使用您在问题中提供的代码,似乎只有在几次传输失败后curl没有继续进行,但实际上仍有数据在缓冲区中排队.你的代码没有足够多次调用curl_multi_info_read.您的代码获取所有成功传输的原因是由于PHP在单个线程上运行,因此脚本挂起等待请求.失败请求的超时不足以使PHP挂起/等待那么长时间,因此while循环执行的迭代次数少于排队数据的次数.