2015-02-04 3 views
2

я построил себе простой объект запроса HTTP, очень простойPHP fgets работает на неопределенный срок

class Request { 

    protected $hostname; 
    protected $port = 80; 
    protected $method = 'GET'; 
    protected $uri = '/'; 
    protected $headers = array(); 
    protected $body; 
    protected $handle; 
    protected $response; 

    public function __construct() { 
     $cookies = null; 
     foreach (\Cookie::getAll() as $k => $v) { 
      $cookies .= urlencode($k) . '=' . urlencode($v) . '; '; 
     } 
     $this->setHeader('Cookie', substr($cookies, 0, -2)); 
    } 

    public function setHostname($name, $ssl = false) { 
     if (filter_var(gethostbyname($name), FILTER_VALIDATE_IP)) { 
      $this->hostname = ($ssl ? 'ssl://' : null) . $name; 
      $this->setHeader('Host', $name); 

      return true; 
     } 
     return false; 
    } 

    public function setPort($port) { 
     if ($port >= 1 && $port <= 65535) { 
      $this->port = (int)$port; 
      return true; 
     } 
     return false; 
    } 

    public function setMethod($method) { 
     $methods = [ 
      'GET' => true, 
      'POST' => true, 
      'PUT' => true, 
      'DELETE' => true 
     ]; 

     if (isset($methods[$method])) { 
      $this->method = strtoupper($method); 
      return true; 
     } 
     return false; 
    } 

    public function setUri($uri) { 
     $this->uri = $uri; 
     return true; 
    } 

    public function setHeader($key, $value) { 
     $this->headers[$key] = $value; 
     return true; 
    } 

    public function removeHeader($key) { 
     unset($this->headers[$key]); 
     return true; 
    } 

    public function setBody($body) { 
     $this->body = $body; 
     return true; 
    } 

    public function send($async = false, $timeout = 30) { 
     $errno = null; 
     $errstr = null; 

     $this->setHeader('Content-Length', strlen($this->body)); 
     if($async){ 
      $this->setHeader('Connection', 'Close'); 
     } 

     $this->response = null; 
     $this->handle = fsockopen($this->hostname, $this->port, $errno, $errstr, $timeout); 
     if (!$this->handle) { 
      throw new \Exception($errstr, $errno); 
     } 

     $headers = null; 
     foreach ($this->headers as $key => $value) { 
      $headers .= $key . ': ' . $value . "\r\n"; 
     } 

     $request = "{$this->method} {$this->uri} HTTP/1.1\r\n{$headers}\r\n" . $this->body; 

     fwrite($this->handle, $request); 

     if ($async) { 
      fclose($this->handle); 
      return; 
     } 

     while (!feof($this->handle)) { 
      echo $this->response . ' dgd<br/><br/><br/>'; 
      $this->response .= fgets($this->handle, 8192); 
     } 
     fclose($this->handle); 
    } 

    public function getResponse() { 
     return $this->response; 
    } 
} 

Когда я посылаю «асинхронный» нет никаких проблем, но когда я пытаюсь получить ответ я получаю максимальное время выполнения превышено ошибка. Я добавил эхо в цикл чтения ответов для отладки, и он запускается только один раз. В чем проблема?

$r = new \Request(); 
$r->setHostname('localhost'); 
$r->setUri('/test.php'); 
$r->send(); 

echo "<pre>" . print_r($r, true) . "</pre>"; 

test.php

echo 'Test complete.'; 

источник Ответ первоначального запроса

dgd<br/><br/><br/><br /> 
<b>Fatal error</b>: Maximum execution time of 30 seconds exceeded in <b>....\Request.php</b> on line <b>98</b><br /> 

где линия 98 является

$this->response .= fgets($this->handle, 8192); 
+0

Я все для выяснения того, как работает материал, но создание собственной библиотеки HTTP не так просто, как вам кажется.Вам также нужно учитывать, что делать, когда вы получаете заголовки '302',' 301', '200 CONTINUE' и т. Д. Есть ли причина, по которой вы не можете использовать cURL или жужжать? – h2ooooooo

+0

Меня это не касается, потому что цель этого объекта - отправлять запросы только моему приложению, и я знаю, какими будут ответы. На данный момент проблема заключается в том, что я не могу прочитать ответ. @ h2ooooooo относительно вашего вопроса - я решил не использовать другие библиотеки именно из-за того, что они более сложны и созданы для обработки различных ситуаций, в то время как мне не нужны все эти функции для моих целей. –

+0

@KarelKubat Я согласен, что имя может быть немного запутанным, в основном это означает отправить запрос и не ждать ответа, с идеей не замедлять реакцию клиента на первоначальный запрос. Что касается 'feof' на закрытых дескрипторах, то невозможно закрыть дескриптор, потому что если' async' истинно, тогда функция вернется, прежде чем попасть в 'feof' –

ответ

2

fgets() будет считывать данные из гнезда и завершена e чтение в нескольких условиях, таких как конец файла, длина - 1 количество прочитанных символов или закрытие сокета.

Первое, что вы могли бы попробовать - изменить свою длину на гораздо меньшее значение, чтобы увидеть, можете ли вы получить несколько чтений. Это скажет вам, работает ли цикл, и fgets() не видит правильного поведения завершения чтения от клиента, который отправляет данные через сокет, который считывается fgets().

Я подозреваю, что клиент, который отправляет данные, не закрывает сокет после отправки данных.

HTTP-протокол действительно предназначен для апатридов. Основная процедура заключается в том, что клиент должен открыть сокет, отправить HTTP-запрос, получить HTTP-ответ от сервера, а затем закрыть сокет. Однако вы можете изменить эту процедуру с надлежащими изменениями на клиенте и на сервере, чтобы поддерживать соединение, отправляющее и получающее данные, такие как JSON или XML.

Поскольку TCP-соединение представляет собой простой поток байтов, любая координация между клиентом и сервером, на котором поддерживается соединение, должна быть реализована с некоторым соглашением со стороны клиента и сервера относительно структуры данных. Использование JSON или XML довольно просто, поскольку сообщения имеют структуру, в которой вы знаете начало и конец сообщения.

Для простого HTTP, использующего стандартную HTTP-процедуру, конец сообщения сообщается закрытием сокета клиентом после получения ответа HTTP от сервера. Это сообщает серверу, что связь выполнена.

+0

Я просто изменил его на '2', затем добавил второй отладчик' echo' после 'fgets()', и он не вышел из строя, результатом является первое превышение ошибки «echo», а время выполнения превысило ошибку. –

+0

Откуда вы знаете, что клиент что-то посылает? Что происходит на стороне клиента? Можете ли вы предоставить вывод отладки с клиентской стороны? –

+0

Я только что проверил журналы доступа, и я вижу, что клиент возвращает ответ намного дольше, чем ожидаемый 'Test complete'', который имеет длину 14 байт, размер в журнале равен 169, поэтому клиент, по-видимому, говорит что-то еще но я не могу понять, что .. –

2

Я пытаюсь проверить ваш код, но вы используете какой-то класс Cookie? Я прокомментировал, что материал из конструктора запускает код, и все, кажется, идет.

я вернусь:

Request Object 
(
[hostname:protected] => test 
[port:protected] => 80 
[method:protected] => GET 
[uri:protected] => /test.php 
[headers:protected] => Array 
    (
     [Host] => test 
     [Content-Length] => 0 
    ) 

[body:protected] => 
[handle:protected] => Resource id #3 
[response:protected] => HTTP/1.1 200 OK 
Date: Wed, 04 Feb 2015 13:51:23 GMT 
Server: Apache/2.4.9 (Unix) PHP/5.4.37 
X-Powered-By: PHP/5.4.37 
Content-Length: 11 
Content-Type: text/html 

Hello World 
) 

Однако я настоятельно призываю вас сделать то же самое, как другие предложили, и просто использовать завиток или, по крайней мере, вызов простой file_get_contents(). Вот простая функция cURL, которую я использую многократно и никогда не сталкивался с проблемой.

function getUrl($u){ 

    // is cURL installed yet? 
    if (!function_exists('curl_init')){ 
     die('Sorry cURL is not installed!'); 
    } 

    // OK cool - then let's create a new cURL resource handle 
    $ch = curl_init(); 

    // Set URL to download 
    curl_setopt($ch, CURLOPT_URL, $u); 


    // User agent 
    curl_setopt($ch, CURLOPT_USERAGENT, "MozillaXYZ/1.0"); 

    // Include header in result? (0 = yes, 1 = no) 
    curl_setopt($ch, CURLOPT_HEADER, 0); 

    // Should cURL return or print out the data? 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 

    // Timeout in seconds 
    curl_setopt($ch, CURLOPT_TIMEOUT, 10); 

    // Download the given URL, and return output 
    $output = curl_exec($ch); 

    // Close the cURL resource, and free system resources 
    curl_close($ch); 

    return $output; 
} 
+0

Спасибо за функцию. Однако в настоящее время я не создаю ничего коммерческого, просто пытаюсь узнать это и то, и неудачно. Но это естественный процесс, я думаю. Я только что сделал открытие, что клиент говорит что-то еще, чем то, что я ожидаю, см. Комментарии RichardChambers. –

Смежные вопросы