$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-encoding
ответа chunked
, тогда вы читаете, пока не столкнетесь с «последним куском» ( \r\n0\r\n
).
Если content-encoding
– gzip
, вы просматриваете заголовок ответа content-length
и читаете много данных, а затем раздуваете его. Если transfer-encoding
также установлено на chunked, тогда вы должны дешифровать декодированный ответ.
Самое простое – создать простой конечный автомат, чтобы прочитать ответ от сокета, пока еще остались данные для ответа.
При чтении фрагментированных данных вы должны прочитать первую длину блока (и любое выделенное расширение), а затем прочитать столько данных, сколько размер блока, и сделать это до последнего фрагмента.
Перефразируй:
\r\n\r\n
) transfer-encoding
разделено, прочитайте и отложите данные по частям. content-length
установлен, вы можете прочитать, что много данных из сокета content-encoding
– gzip, распакуйте прочитанные данные После того, как вы выполнили вышеуказанные шаги, вы должны прочитать весь ответ, и теперь вы можете отправить другой HTTP-запрос в один и тот же сокет и повторить этот процесс.
С другой стороны, если вам не нужна абсолютная потребность в подключении keep-alive, просто установите Connection: close
запрос, и вы можете спокойно читать while (!feof($f))
.
У меня нет PHP-кода для чтения и анализа HTTP-ответов на данный момент (я просто использую cURL), но если вы хотите увидеть фактический код, сообщите мне, и я могу что-то с этим справиться. Я мог бы также ссылаться на некоторый код C #, который я сделал, который делает все вышеперечисленное.
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; }
Следующий код работает без проблем для меня:
<?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;"; ?>
Самое смешное в том, что, не сохранившись, этот пример подходит для меня. Можете ли вы добавить пример, который может быть просто скопирован и вставлен, и показывает вашу ошибку?