2014-09-11 1 views
1

PHP (5.3.2 & 5.4.4)에서 비 차단 스트림을 열려고합니다. 나는 다음을 수행 : PHP 파일에URL에서 fopen이 PHP 스크립트 실행을 차단합니다.

$fp = fopen($url, 'r'); 
if ($fp === false) 
    return false; 
print('stream opened'.PHP_EOL); 
stream_set_blocking($fp, 0); 

는 URL 포인트 :

<?php sleep(10); ?> 
<html><body>Hello</body></html> 

문제는()는 fopen 내가 설정 비 블로킹과 스트림도 수 있어요 전에 차단하는 것이다. 실제로 stream opened 메시지는 10 초 후에 인쇄되며 직접 인쇄되지는 않습니다.

+1

'stream_set_blocking'에 관한 문서에 따르면 소켓과 일반 파일 만 지원합니다. 귀하의 URL 리소스가 해당 리소스가 아닙니다. – phpisuber01

+0

TCP 연결이 소켓 스트림이 아닌가요? 어쨌든, 여기 freop()이 아니라, 여기에서 블록하는 fopen입니다 (나는 10 초 후에 'stream opened'라는 메시지를 읽었습니다). – Mat

+0

당신의'fopen'은 당신의 URL에서 핸드 셰이크가 일어나기를 기다리고 있습니다. PHP 스크립트에'sleep (10)'이 있기 때문에 10 초를 기다리고 있습니다. 스트림에는 핸드 셰이크가 필요하고 영구 연결이 필요합니다. PHP 스크립트가 10 초 동안 기다리고 응답을 보내고 즉시 연결을 닫습니다. 이것은 PHP의 기본 동작입니다. PHP를 사용하여 소켓 스트림을 만들 수는 있지만 두 줄짜리 냅킨 스크립트가 아닙니다. – phpisuber01

답변

1

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); 
} 
관련 문제