如果连接保持活动状态,如何读取直到流 PHP 结束


if connection is keep alive how to read until end of stream 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一起工作。

你如何在不拖延的情况下做到这一点?

这取决于

响应,如果响应的transfer-encodingchunked,那么你阅读直到遇到"最后一个块"('r'n0'r'n)。

如果content-encodinggzip,那么你查看content-length响应标头并读取那么多数据,然后膨胀它。 如果transfer-encoding也设置为分块,则必须对解码的响应进行分块。

最简单的方法是构建一个简单的状态机,在仍有数据留给响应时从套接字读取响应。

读取分块

数据时,应读取第一个分块长度(以及任何分块扩展),然后读取与分块大小一样多的数据,并一直读取到最后一个分块。

换句话说:

  • 读取 HTTP 响应标头(读取小块数据,直到遇到'r'n'r'n
  • 将响应标头解析为数组
  • 如果transfer-encoding是分块的,请逐段读取和分块数据。
  • 如果设置了content-length标头,则可以从套接字读取大量数据
  • 如果content-encoding为 gzip,则解压读取的数据

执行上述步骤后,您应该已经阅读了整个响应,现在可以在同一套接字上发送另一个 HTTP 请求并重复该过程。

另一方面,除非您绝对需要保持活动连接,否则只需在请求中设置Connection: close,您就可以安全地读取while (!feof($f))

目前没有任何用于读取和解析HTTP响应的PHP代码(我只使用cURL),但是如果您想查看实际代码,请告诉我,我可以做一些事情。 我还可以向您推荐我制作的一些 C# 代码,这些代码可以完成上述所有操作。

编辑:这是使用fsockopen发出HTTP请求并演示读取保持活动连接以及分块编码和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;
}

以下代码对我来说没有任何问题:

<?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;";
?>

有趣的是,如果没有保持活力,这个例子对我来说就会停滞不前。你能添加一个可以复制和粘贴并显示你的错误的例子吗?