Если соединение сохраняется, как читать до конца потока php

$f = fsockopen("www....",80,$x,$y);

fwrite("GET request HTTP/1.1\r\nConnection: keep-alive\r\n\r\n");

while($s = fread($f,1024)){
    ...
}

Вышеупомянутые киоски из-за Connection: keep-alive и работают с Connection: close.

Как вы это делаете без остановки?

Ответ 1

Это зависит от ответа, если transfer-encoding ответа chunked, тогда вы читаете, пока не встретите "последний кусок" (\r\n0\r\n).

Если content-encoding - gzip, вы можете посмотреть заголовок ответа content-length и прочитать много данных, а затем раздуть его. Если параметр transfer-encoding также установлен в chunked, вы должны дешифровать декодированный ответ.

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

При чтении фрагментированных данных вы должны прочитать первую длину блока (и любое выделенное расширение), а затем прочитать столько данных, сколько размер блока, и сделать это до последнего фрагмента.

Поставьте другой способ:

  • Прочитайте заголовки ответа HTTP (прочитайте небольшие фрагменты данных, пока не встретите \r\n\r\n)
  • Разберите заголовки ответов в массив
  • Если transfer-encoding помечен, прочитайте и отбросите данные по частям.
  • Если заголовок content-length установлен, вы можете прочитать, что много данных из сокета
  • Если content-encoding является gzip, распакуйте прочитанные данные

После выполнения вышеуказанных шагов вы должны прочитать весь ответ, и теперь вы можете отправить другой HTTP-запрос в тот же сокет и повторить процесс.

С другой стороны, если у вас нет абсолютной потребности в подключении keep-alive, просто установите Connection: close в запросе, и вы можете спокойно читать while (!feof($f)).

У меня нет PHP-кода для чтения и анализа HTTP-ответов на данный момент (я просто использую cURL), но если вы хотите увидеть фактический код, сообщите мне, и я могу что-то с этим справиться. Я мог бы также ссылаться на некоторый код С#, который я сделал, который делает все вышеперечисленное.

EDIT: Вот рабочий код, который использует fsockopen для выдачи HTTP-запроса и демонстрирует чтение keep-alive соединений с возможностью кодирования и сжатия gzip. Протестировано, но не подвергается пыткам - используйте на свой страх и риск!!!

<?php

/**
 * PHP HTTP request demo
 * Makes HTTP requests using PHP and fsockopen
 * Supports chunked transfer encoding, gzip compression, and keep-alive
 *
 * @author drew010 <http://stackoverflow.com/questions/11125463/if-connection-is-keep-alive-how-to-read-until-end-of-stream-php/11812536#11812536>
 * @date 2012-08-05
 * Public domain
 *
 */

error_reporting(E_ALL);
ini_set('display_errors', 1);

$host = 'www.kernel.org';

$sock = fsockopen($host, 80, $errno, $errstr, 30);

if (!$sock) {
    die("Connection failed.  $errno: $errstr\n");
}

request($sock, $host, 'GET', '/');

$headers = readResponseHeaders($sock, $resp, $msg);
$body    = readResponseBody($sock, $headers);

echo "Response status: $resp - $msg\n\n";

echo '<pre>' . var_export($headers, true) . '</pre>';
echo "\n\n";
echo $body;

// if the connection is keep-alive, you can make another request here
// as demonstrated below

request($sock, $host, 'GET', '/kernel.css');
$headers = readResponseHeaders($sock, $resp, $msg);
$body    = readResponseBody($sock, $headers);

echo "Response status: $resp - $msg\n\n";

echo '<pre>' . var_export($headers, true) . '</pre>';
echo "\n\n";
echo $body;


exit;

function request($sock, $host, $method = 'GET', $uri = '/', $params = null)
{
    $method = strtoupper($method);
    if ($method != 'GET' && $method != 'POST') $method = 'GET';

    $request = "$method $uri HTTP/1.1\r\n"
              ."Host: $host\r\n"
              ."Connection: keep-alive\r\n"
              ."Accept-encoding: gzip, deflate\r\n"
              ."\r\n";

    fwrite($sock, $request);
}

function readResponseHeaders($sock, &$response_code, &$response_status)
{
    $headers = '';
    $read    = 0;

    while (true) {
        $headers .= fread($sock, 1);
        $read    += 1;

        if ($read >= 4 && $headers[$read - 1] == "\n" && substr($headers, -4) == "\r\n\r\n") {
            break;
        }
    }

    $headers = parseHeaders($headers, $resp, $msg);

    $response_code   = $resp;
    $response_status = $msg;

    return $headers;
}

function readResponseBody($sock, array $headers)
{
    $responseIsChunked = (isset($headers['transfer-encoding']) && stripos($headers['transfer-encoding'], 'chunked') !== false);
    $contentLength     = (isset($headers['content-length'])) ? $headers['content-length'] : -1;
    $isGzip            = (isset($headers['content-encoding']) && $headers['content-encoding'] == 'gzip') ? true : false;
    $close             = (isset($headers['connection']) && stripos($headers['connection'], 'close') !== false) ? true : false;

    $body = '';

    if ($contentLength >= 0) {
        $read = 0;
        do {
            $buf = fread($sock, $contentLength - $read);
            $read += strlen($buf);
            $body .= $buf;
        } while ($read < $contentLength);

    } else if ($responseIsChunked) {
        $body = readChunked($sock);
    } else if ($close) {
        while (!feof($sock)) {
            $body .= fgets($sock, 1024);
        }
    }

    if ($isGzip) {
        $body = gzinflate(substr($body, 10));
    }

    return $body;
}

function readChunked($sock)
{   
    $body = '';

    while (true) {
        $data = '';

        do {
            $data .= fread($sock, 1);
        } while (strpos($data, "\r\n") === false);

        if (strpos($data, ' ') !== false) {
            list($chunksize, $chunkext) = explode(' ', $data, 2);
        } else {
            $chunksize = $data;
            $chunkext  = '';
        }

        $chunksize = (int)base_convert($chunksize, 16, 10);

        if ($chunksize === 0) {
            fread($sock, 2); // read trailing "\r\n"
            return $body;
        } else {
            $data    = '';
            $datalen = 0;
            while ($datalen < $chunksize + 2) {
                $data .= fread($sock, $chunksize - $datalen + 2);
                $datalen = strlen($data);
            }

            $body .= substr($data, 0, -2); // -2 to remove the "\r\n" before the next chunk
        }
    } // while (true)
}

function parseHeaders($headers, &$response_code = null, &$response_message = null)
{
    $lines  = explode("\r\n", $headers);
    $return = array();

    $response = array_shift($lines);

    if (func_num_args() > 1) {
        list($proto, $code, $message) = explode(' ', $response, 3);

        $response_code    = $code;

        if (func_num_args() > 2) {
            $response_message = $message;
        }
    }

    foreach($lines as $header) {
        if (trim($header) == '') continue;
        list($name, $value) = explode(':', $header, 2);

        $return[strtolower(trim($name))] = trim($value);
    }

    return $return;
}

Ответ 2

Следующий код работает без проблем для меня:

<?php
$f = fsockopen("www.google.de",80);
fwrite($f,"GET / HTTP/1.1\r\n Connection: keep-alive\r\n\r\n");

while($s = fread($f,1024)){
    echo "got: $s";
}
echo "finished;";
?>

Самое смешное, что без сохранить этот пример для меня. Можете ли вы добавить пример, который можно просто скопировать и вставить и отобразить ошибку?