2012-06-20 13 views
10
$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)){ 
    ... 
} 

Los puestos anteriores, debido a la Connection: keep-alive, y trabaja con Connection: close.si la conexión es mantener viva la forma de leer hasta el final del flujo php

¿Cómo lo haces sin atascos?

+0

Como un lado, te das cuenta de que tienes un error de cotización, ¿no? – Daedalus

+1

@Daedalus Puedo verificar que el error de cotización no es el problema. Puse la recompensa porque tengo un problema similar y ya he perdido demasiado tiempo tratando de arreglarlo :) – rdlowrey

+0

Buena captura, @Daedalus, no entendí eso. Sin embargo, estoy con Rdlowrey, ese tipo de error habría sido diferente. –

Respuesta

15

Depende de la respuesta, si el transfer-encoding de la respuesta es , entonces lee hasta que encuentre el "último trozo" (\r\n0\r\n).

Si el content-encoding es gzip, mira el encabezado de respuesta content-length y lee esa cantidad de datos y luego inflarlo. Si el transfer-encoding también está configurado para fragmentarse, debe descifrar la respuesta decodificada.

Lo más fácil es construir una máquina de estado simple para leer la respuesta desde el socket mientras aún quedan datos para la respuesta.

Al leer datos fragmentados, debe leer la primera longitud de fragmento (y cualquier extensión fragmentada) y luego leer tantos datos como el tamaño de fragmento, y hacerlo hasta el último fragmento.

Dicho de otra manera:

  • leer las cabeceras de respuesta HTTP (leer pequeños fragmentos de datos hasta que se encuentra con \r\n\r\n)
  • analizar los encabezados de respuesta en una matriz
  • Si está fragmentada del transfer-encoding, leer y dechunk la información pieza por pieza.
  • Si la cabecera content-length se establece, se puede leer que muchos datos de la toma de
  • Si el content-encoding es gzip, descomprimir los datos de lectura

Una vez que haya realizado los pasos anteriores, debe haber leído toda la respuesta y ahora puede enviar otra solicitud HTTP en el mismo socket y repetir el proceso.

Por otro lado, a menos que tenga la necesidad absoluta de una conexión para mantener vivo, simplemente configure Connection: close en la solicitud y puede leer con seguridad while (!feof($f)).

No tengo ningún código PHP para leer y analizar las respuestas HTTP en este momento (solo uso cURL) pero si desea ver el código real, hágamelo saber y puedo resolver algo. También podría remitirte a algún código de C# que haya hecho que haga todo lo anterior.

EDITAR: Aquí está el código de trabajo que usa fsockopen para emitir una solicitud HTTP y demostrar la lectura de las conexiones keep-alive con la posibilidad de codificación fragmentada y compresión gzip. Probado, pero no torturado, ¡utilícelo bajo su propio riesgo!

<?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; 
} 
+0

En este caso, la interpretación de codificaciones de transferencia fragmentada y codificaciones de contenido comprimido no está en cuestión. Además, HTTP/1.1 describe específicamente que los servidores NO DEBEN enviar un encabezado 'Content-Length' si se establece un encabezado' Transfer-Encoding'. El problema en cuestión es por qué el ciclo se detiene después de los primeros 1024 bytes en el código de la lista y nunca regresa * cuando keep-alive es el comportamiento deseado *. Puse la recompensa porque entiendo todas las cosas en tu respuesta, pero no puedo entender cómo hacer que funcione con 'Conexión: keep-alive' ... – rdlowrey

+0

Ya veo, entonces es más probable que haya un problema de PHP si su estancamiento, debería ser capaz de leer al menos toda la respuesta antes de los bloques 'fread' porque no hay más datos disponibles. Pero debe tenerse en cuenta que una vez que ha leído toda la respuesta, volver a llamar 'fread' se bloqueará indefinidamente (o hasta que el socket se agote), por lo que con una conexión keep-alive, no podrá usar' fread' en un bucle como eso. – drew010

+1

@rdlowrey: Si la conexión se mantiene activa y continúa con su ciclo de lectura ingenua, PHP intentará continuar leyendo hasta que se cierre la conexión. Si 'Connection: close' está configurado, esto está bien. Si se establece 'Connection: keep-alive', debe realizar un procesamiento adicional para saber cuándo dejar de leer. – icktoofay

0

El código siguiente funciona sin ningún tipo de problema para mí:

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

Lo curioso es que sin keep-alive este ejemplo establos para mí. ¿Puede agregar un ejemplo que solo puede copiar & pegado y muestra su error?

+2

La respuesta es una mala solicitud. supongo que tienes un espacio extra antes de la palabra conexión. Además, después de corregir el espacio y después de que el ciclo mostrara todos los datos, se suspendió porque Fread está tratando de no leer datos. –

Cuestiones relacionadas