URL에 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
TCP 연결이 소켓 스트림이 아닌가요? 어쨌든, 여기 freop()이 아니라, 여기에서 블록하는 fopen입니다 (나는 10 초 후에 'stream opened'라는 메시지를 읽었습니다). – Mat
당신의'fopen'은 당신의 URL에서 핸드 셰이크가 일어나기를 기다리고 있습니다. PHP 스크립트에'sleep (10)'이 있기 때문에 10 초를 기다리고 있습니다. 스트림에는 핸드 셰이크가 필요하고 영구 연결이 필요합니다. PHP 스크립트가 10 초 동안 기다리고 응답을 보내고 즉시 연결을 닫습니다. 이것은 PHP의 기본 동작입니다. PHP를 사용하여 소켓 스트림을 만들 수는 있지만 두 줄짜리 냅킨 스크립트가 아닙니다. – phpisuber01