當在網址上做一個fopen
時,HTTP標頭在那個時候發送。由於沒有上下文被拒絕(並且不可能使用非阻塞選項配置上下文),所以fopen
等待發送http報頭和塊。 解決方法是使用fsockopen
,它只打開tcp連接,不起作用。這種方法的缺點是必須手動創建HTTP請求。
這是一個(可優化的)實現,它以非阻塞的方式從url中讀取數據。
function parse_http_url($url)
{
$parts = parse_url($url);
if ($parts === false) return false;
if (!isset($parts['scheme']))
$parts['scheme'] = 'http';
if ($parts['scheme'] !== 'http' && $parts['scheme'] !== 'https')
return false;
if (!isset($parts['port']))
$parts['port'] = ($parts['scheme'] === 'http') ? 80 : 443;
if(!isset($parts['path']))
$parts['path'] = '/';
$parts['uri'] = $parts['path'];
if (!empty($parts['query']))
$parts['uri'] .= '?'.$parts['query'];
return $parts;
}
function url_get_contents($url, $options = null) {
if(!($url_parts = parse_http_url($url))) return false;
$timeout = intval(@$options['http']['timeout']);
if (!($fp = fsockopen($url_parts['host'], $url_parts['port'], $errno, $errstr, $timeout))) return false;
stream_set_blocking($fp, 0);
if($timeout > 0) {
stream_set_timeout($fp, $timeout);
$sleep_time = (($timeout * 1000000)/100); # 1% of timeout in ms
$stop_time = microtime(true) + $timeout;
} else {
$sleep_time = 10000; # 10 ms
}
if (!isset($options['http']['method'])) $options['http']['method'] = 'GET';
if (!isset($options['http']['header'])) $options['http']['header'] = '';
$request = "{$options['http']['method']} {$url_parts['uri']} HTTP/1.1\r\n{$options['http']['header']}\r\n";
if (fwrite($fp, $request) === false) {
fclose($fp);
return false;
}
$content = '';
$buff_size = 4096;
do {
$rd = fread($fp, $buff_size);
if ($rd === false) {
fclose($fp);
return false;
}
$content .= $rd;
$meta = stream_get_meta_data($fp);
if ($meta['eof']) {
fclose($fp);
if(empty($content)) return false;
// HTTP headers should be separated with \r\n only but lets be safe
$content = preg_split('/\r\n|\r|\n/', $content);
$resp = explode(' ', array_shift($content));
$code = isset($resp[1]) ? intval($resp[1]) : 0;
if ($code < 200 || $code >= 300) {
$message = isset($resp[2]) ? $resp[2] : 'Unknown error';
trigger_error("Error {$code} {$message}", E_USER_WARNING);
return false;
}
// Skip headers
while (!empty($content) && array_shift($content) !== '');
return implode("\n", $content);
}
if ($meta['timed_out']) {
fclose($fp);
return false;
}
if (isset($stop_time) && microtime(true) >= $stop_time) {
fclose($fp);
return false;
}
if ($meta['unread_bytes'] === 0) {
usleep($sleep_time);
}
} while(true);
}
來源
2014-09-11 14:58:07
Mat
根據'stream_set_blocking'上的文檔,它只支持套接字和普通文件。你的URL資源不是那些。 – phpisuber01 2014-09-11 13:39:40
不是TCP連接的套接字流嗎?無論如何,這是封鎖在這裏(我讀了10秒後'流'打開的信息),而不是freads()。 – Mat 2014-09-11 14:01:35
您的'fopen'正在等待您的URL發生握手。它等待10秒,因爲你的PHP腳本有一個「睡眠(10)」。流需要握手,然後持續打開連接。您的PHP腳本正在等待10秒,發送響應並立即關閉連接。這是PHP的默認行爲。您可以使用PHP創建套接字流,但不會成爲雙行餐巾腳本。 – phpisuber01 2014-09-11 14:32:56