如果连接保持活动状态,如何读取php直到流结束
上述暂停是因为如果连接保持活动状态,如何读取php直到流结束,php,http,Php,Http,上述暂停是因为连接:保持活动状态,并使用连接:关闭 如何在不暂停的情况下执行此操作?这取决于响应,如果响应的传输编码是分块的,则您将一直阅读,直到遇到“最后一块”(\r\n0\r\n) 如果内容编码为gzip,则查看内容长度响应标题,读取大量数据,然后将其充气。如果传输编码也设置为chunked,则必须取消对解码响应的chunk 最简单的方法是构建一个简单的状态机,以便在仍然有响应数据的情况下从套接字读取响应 在读取分块数据时,您应该读取第一个分块长度(以及任何分块扩展),然后读取与分块大小相同
连接:保持活动状态
,并使用连接:关闭
如何在不暂停的情况下执行此操作?这取决于响应,如果响应的
传输编码是分块的,则您将一直阅读,直到遇到“最后一块”(\r\n0\r\n
)
如果内容编码
为gzip
,则查看内容长度
响应标题,读取大量数据,然后将其充气。如果传输编码
也设置为chunked,则必须取消对解码响应的chunk
最简单的方法是构建一个简单的状态机,以便在仍然有响应数据的情况下从套接字读取响应
在读取分块数据时,您应该读取第一个分块长度(以及任何分块扩展),然后读取与分块大小相同的数据,直到最后一个分块
换句话说:
- 读取HTTP响应头(读取小块数据,直到遇到
\r\n\r\n
)
- 将响应头解析为数组
- 如果
传输编码
被分块,则逐块读取并解块数据
- 如果设置了
内容长度
标题,则可以从套接字读取那么多数据
- 如果
内容编码为gzip,则解压缩读取的数据
一旦执行了上述步骤,您应该已经读取了整个响应,现在可以在同一套接字上发送另一个HTTP请求并重复该过程
另一方面,除非您绝对需要保持活动连接,否则只需在请求中设置connection:close
,您就可以在(!feof($f))
时安全地读取
目前我没有任何PHP代码用于读取和解析HTTP响应(我只使用cURL),但是如果您想查看实际的代码,请告诉我,我可以做一些工作。我还可以让你参考我编写的一些C代码,它们可以完成上述所有功能
编辑:下面是使用fsockopen
发出HTTP请求的工作代码,并演示如何读取具有分块编码和gzip压缩可能性的保持活动连接。经过测试,但不受折磨-使用风险自负
$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)){
...
}
以下代码对我来说没有任何问题:
有趣的是,如果没有保持活力,这个例子对我来说会停滞不前。
你能添加一个可以复制粘贴并显示错误的示例吗?顺便说一句,你知道你有报价错误,对吗?@Daedalus我可以验证报价错误不是问题所在。我之所以悬赏,是因为我有一个类似的问题,并且已经浪费了太多时间试图解决它:)抓得好,@Daedalus,我没抓到。不过,我支持rdlowrey,这种错误会有所不同。在这种情况下,解释分块传输编码和压缩内容编码是没有问题的。此外,HTTP/1.1特别指出,如果设置了传输编码
头,则服务器不应发送内容长度
头。目前的问题是,为什么循环在列出的代码中的前1024个字节之后停止,并且在keep-alive是所需行为时从不返回。我之所以选择悬赏是因为我理解你答案中的所有内容,但我不知道如何使用Connection:keep alive
…我明白了,如果它停止运行,那么更有可能出现PHP问题,他至少应该能够在fread
块之前读取整个响应,因为没有更多可用数据。但是需要注意的是,一旦他读取了整个响应,再次调用fread
将无限期地阻塞(或者直到套接字超时),因此,对于保持活动的连接,您不能在这样的循环中使用fread
。@rdlowrey:如果连接保持活动,并且您继续您天真的读取循环,PHP将尝试继续读取,直到连接关闭。如果设置了Connection:close
,则这是正常的。如果设置了Connection:keep-alive
,您需要进行额外的处理,以知道何时停止读取。@rdlowrey如承诺的那样,我终于添加了代码。我把它分成了一些函数,但仅此而已,我认为现在没有必要把它变成一个类。希望它能帮助你。这将支持keep-alives,可以在同一个连接上发送多个请求,读取分块响应并解压缩gzip。@JayapalChandran它不仅仅是PHP,它是keep-alive连接发送数据的本质。如果在c/c++中调用recv
,并且它是一个阻塞接收,那么程序将挂起,因为它正在尝试在套接字上没有更多数据时进行recv。您可以通过使用非阻塞recv或设置超时来绕过它,但这并不理想,因为通过读取标题,您可以知道何时读取了整个响应,而不必调用recv
。响应是错误的请求。我假设在单词connection之前有一个额外的空格。除此之外,在更正空格和循环显示所有数据后,由于fread尝试不读取任何数据,因此它被挂起。
<?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;";
?>