Как работает Keep-Alive?
Код:
<?
$r = "GET http://site.com/index.php HTTP/1.1\r\n";
$r .= "Accept: */*\r\n";
$r .= "Accept-Language: ru\r\n";
$r .= "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; WebMoney Advisor; Pivim Multibar; .NET CLR 2.0.50727; yie8)\r\n";
$r .= "Accept-Encoding: gzip, deflate\r\n";
$r .= "Connection: Keep-Alive\r\n";
$r .= "Host: site.com\r\n";
$r .= "\r\n";
$fp = fsockopen("ip",80,$errno,$errstr,30);
if ($fp) {
fputs($fp,$r);
$bd = "";
$hed = "";
while (($fr = fgets($fp)) != "\r\n") $hed .= $fr;
while (!feof($fp)) {
$bd .= fread($fp,4096);
if (substr($bd, -9)=="\r\n\r\n0\r\n\r\n") {
exit;
}
}
$r = "GET http://site.com/img.gif HTTP/1.1\r\n";
$r .= "Accept: */*\r\n";
$r .= "Accept-Language: ru\r\n";
$r .= "Referer: http://site.com/index.php\r\n";
$r .= "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; WebMoney Advisor; Pivim Multibar; .NET CLR 2.0.50727; yie8)\r\n";
$r .= "Accept-Encoding: gzip, deflate\r\n";
$r .= "Connection: Keep-Alive\r\n";
$r .= "Host: site.com\r\n";
$r .= "\r\n";
fputs($fp,$r);
while (($fr = fgets($fp)) != "\r\n") $hed .= $fr;
while (!feof($fp)) {
$bd .= fread($fp,4096);
if (substr($bd, -9)=="\r\n\r\n0\r\n\r\n") {
exit;
}
}
echo $hed;
}
fclose($fp);
$r = "GET http://site.com/index.php HTTP/1.1\r\n";
$r .= "Accept: */*\r\n";
$r .= "Accept-Language: ru\r\n";
$r .= "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; WebMoney Advisor; Pivim Multibar; .NET CLR 2.0.50727; yie8)\r\n";
$r .= "Accept-Encoding: gzip, deflate\r\n";
$r .= "Connection: Keep-Alive\r\n";
$r .= "Host: site.com\r\n";
$r .= "\r\n";
$fp = fsockopen("ip",80,$errno,$errstr,30);
if ($fp) {
fputs($fp,$r);
$bd = "";
$hed = "";
while (($fr = fgets($fp)) != "\r\n") $hed .= $fr;
while (!feof($fp)) {
$bd .= fread($fp,4096);
if (substr($bd, -9)=="\r\n\r\n0\r\n\r\n") {
exit;
}
}
$r = "GET http://site.com/img.gif HTTP/1.1\r\n";
$r .= "Accept: */*\r\n";
$r .= "Accept-Language: ru\r\n";
$r .= "Referer: http://site.com/index.php\r\n";
$r .= "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; WebMoney Advisor; Pivim Multibar; .NET CLR 2.0.50727; yie8)\r\n";
$r .= "Accept-Encoding: gzip, deflate\r\n";
$r .= "Connection: Keep-Alive\r\n";
$r .= "Host: site.com\r\n";
$r .= "\r\n";
fputs($fp,$r);
while (($fr = fgets($fp)) != "\r\n") $hed .= $fr;
while (!feof($fp)) {
$bd .= fread($fp,4096);
if (substr($bd, -9)=="\r\n\r\n0\r\n\r\n") {
exit;
}
}
echo $hed;
}
fclose($fp);
Нет ответа от сервера... Maximum execution time of 30 seconds exceeded... Если закрываю и открываю заново, тогда все работает.. Пробовал добавлять "Keep-Alive: timeout=5; max=100\r\n" не помогло. Как быть?
Некоторые серверы могут закрывать соединение несмотря на keep-alive. Или этот точно нормально работает с keep-alive?
А curl нельзя использовать? Он проще.
Этот точно работает т.к. если посылать один запрос приходит ответ с "Keep-Alive: timeout=5, max=100 Connection: Keep-Alive".
Цитата: int
А curl нельзя использовать? Он проще.
можно но не хочется...
Цитата:
Warning
If a connection opened by fsockopen() wasn't closed by the server, feof() will wait until a timeout has been reached to return TRUE.
Тогда при чтении первого ответа всё виснет.
Ну так по моему при keep-alive сервер в конце тела возвращает "\r\n\r\n0\r\n\r\n". У меня на это exit. Виснуть не должно!
Тем более если посылаю один запрос, сервер также не закрывает соединение и ничего не виснет, так что это тут не причем...
Перепробовал pfsockopen, socket_set_blocking + stream_set_timeout... неужели через fsock нельзя работать с keep-alive?
Цитата: Snk
Ну так по моему при keep-alive сервер в конце тела возвращает "\r\n\r\n0\r\n\r\n". У меня на это exit. Виснуть не должно!
Кто сказал? Если Transfer-Encoding: chunked, то будет:
\r\n потом 0, потом (теоретически может быть) точка с запятой, фигня после неё и так далее (см. в Google), а может и этого не быть, так что или надо подробно разбираться и парсить http, или посмотреть, как отвечает именно этот сервер и надеяться, что так будет всегда, или не fsock. В любом случае вручную отправить эти заголовки и посмотреть ответ не помешает.
1) Нужно учитывать заголовки полученые в режиме Keep-Alive.
Если прислали Connection: Close. Значит сервер скорее всего не поддерживает этот режим или что-то не правильно. Может быть вместо Алива вы послали Close. Или со временем проблема. Лучше слать заголовок Keep-Alive: 300 или сколько угодно вам секунд ожидания от вас "весточки".
После первого запроса, по правилам Алива сервер должен присылать Content-Length: 3232 (кол. байт кода, они нам).
Соответственно столько и считываем данных с сокета, по другому можно определить конец, но не всегода удобно и не без подводных камней. Опять же зависит от заголовков.
После точного считывания проверка сокета на !feof($fp), должно корректно выкочить из цикла, если есть таковой. А потом опять без закрытия шлем запросы в этот же дескриптор и опять читаем. Вот он Alive!
2) Если вы слали заголовок, что не плохо бы сжимать данные - Accept-Encoding: gzip, deflate (непременно лучше слать!!!)
(для оперы - deflate, gzip, x-gzip, identity, *;q=0)
Тогда вам прийдет ЗИП с большой вероятностью и заголовок с указанием, что данные сжаты gzip. Если вы все делайте через fsockopen и т.п. вам не обойтись без расколачивания и расзиповки:
Transfer-Encoding: chunked (если пришлют это, то надо закалачивать), и соотв. проверить пришло ли это:
Content-Encoding: gzip
Тогда надо еще и расзиповывать.
3) И так, если есть заголовки:
Transfer-Encoding: chunked
Content-Encoding: gzip
Надо расскодировать и расзиповывать.
Если один из этих, тогда только одно действие.
Если ничего, значит ничего.
Данные chunked - в самом начале 3-и байта hex размера и превод стоки, за тем следует данные. Если Данные зазипованы, то первый фрагмент надо пропустить, т.к. он должен быть 10 байт, как я понимаю эти данные не нужны вообще, как и концовка, которая может состоять из 8-и байт. Я лично пропускаю размер данных которые меньше 11 байт, это не правильно, но логически и не должно привести к ошибке.
Еще раз, мы рассматриваем данные в chunked и в зипе:
Оболочка chunked, вернее блоки chunked, каждый блок начинается с размера данных в нем и перевод стоки. Потом сначала 10 байт резерва Гзипа, его пропускаем, потом опять перевод стоки \r\n и читаем 3-и 16-иричных числа (которые переводим в десятичку и указываем в функцию), если код 20 то пустота. Затем \r\n а далее полезные данные, их теперь надо расзиповать gzinflate($str,$len), и так каждый блок.
Сначала надо склеить если chunked заголовок прислан, а потом всю строку расзиповывать, первые 10 байт пропускаем:
$h - массив заголовков.
$d - принятные вэб данные.
На выходе нормальный результат.
Код:
<?php
function decode_gzip($h,$d,$rn="\r\n"){
if (isset($h['Transfer-Encoding'])){
$lrn = strlen($rn);
$str = '';
$ofs=0;
do{
$p = strpos($d,$rn,$ofs);
$len = hexdec(substr($d,$ofs,$p-$ofs));
$str .= substr($d,$p+$lrn,$len);
$ofs = $p+$lrn*2+$len;
}while ($d[$ofs]!=='0');
$d=$str;
}
if (isset($h['Content-Encoding'])) $d = gzinflate(substr($d,10));
return $d;
}
?>
function decode_gzip($h,$d,$rn="\r\n"){
if (isset($h['Transfer-Encoding'])){
$lrn = strlen($rn);
$str = '';
$ofs=0;
do{
$p = strpos($d,$rn,$ofs);
$len = hexdec(substr($d,$ofs,$p-$ofs));
$str .= substr($d,$p+$lrn,$len);
$ofs = $p+$lrn*2+$len;
}while ($d[$ofs]!=='0');
$d=$str;
}
if (isset($h['Content-Encoding'])) $d = gzinflate(substr($d,10));
return $d;
}
?>
Данные могут быть повреждены или еще какие-то неожиданости.
Так же в функции gzinflate есть баг который убран только в самых современных сборках.
Код:
<?php
function decode_gzip($h,$d,$rn="\r\n"){
if (isset($h['Transfer-Encoding'])){
$lrn = strlen($rn);
do{
@$p = strpos($d,$rn,$ofs);
if($p===false){$str=''; break;}
$len = hexdec(substr($d,$ofs,$p-$ofs));
$str .= substr($d,$p+$lrn,$len);
$ofs = $p+$lrn*2+$len;
}while ($d[$ofs]!=='0');
$d=$str;
}
//$slupd = unpack('L',substr($d,-4));
if (isset($h['Content-Encoding']))
{
static $lim_mem;
if(!$lim_mem) $lim_mem = ini_get('memory_limit')<<18;
@$d = gzinflate(substr($d,10),$lim_mem);
}
return $d;
}
?>
function decode_gzip($h,$d,$rn="\r\n"){
if (isset($h['Transfer-Encoding'])){
$lrn = strlen($rn);
do{
@$p = strpos($d,$rn,$ofs);
if($p===false){$str=''; break;}
$len = hexdec(substr($d,$ofs,$p-$ofs));
$str .= substr($d,$p+$lrn,$len);
$ofs = $p+$lrn*2+$len;
}while ($d[$ofs]!=='0');
$d=$str;
}
//$slupd = unpack('L',substr($d,-4));
if (isset($h['Content-Encoding']))
{
static $lim_mem;
if(!$lim_mem) $lim_mem = ini_get('memory_limit')<<18;
@$d = gzinflate(substr($d,10),$lim_mem);
}
return $d;
}
?>
Код:
function decode_gzip($h,$d,$rn="\r\n"){
if (isset($h['Transfer-Encoding:'])){
$lrn = strlen($rn);
$ofs=0; $str='';
do{
@$p = strpos($d,$rn,$ofs);
if($p===false){$str=''; break;}
$len = hexdec(substr($d,$ofs,$p-$ofs));
$str .= substr($d,$p+$lrn,$len);
$ofs = $p+$lrn*2+$len;
}while ($d[$ofs]!=='0');
$d=$str;
}
if (strlen($d)&&isset($h['Content-Encoding:']))
{
if($h['Content-Encoding:']==='gzip'){
static $lim_mem;
if(!$lim_mem) $lim_mem = ini_get('memory_limit')<<20;
$slupd = unpack('L',substr($d,-4));
@$t = ($slupd[1]<$lim_mem)?gzinflate(substr($d,10),$slupd[1]):'';
if(strlen($t)) return $t;
}
@$d=gzinflate($d);
}
return $d;
}
if (isset($h['Transfer-Encoding:'])){
$lrn = strlen($rn);
$ofs=0; $str='';
do{
@$p = strpos($d,$rn,$ofs);
if($p===false){$str=''; break;}
$len = hexdec(substr($d,$ofs,$p-$ofs));
$str .= substr($d,$p+$lrn,$len);
$ofs = $p+$lrn*2+$len;
}while ($d[$ofs]!=='0');
$d=$str;
}
if (strlen($d)&&isset($h['Content-Encoding:']))
{
if($h['Content-Encoding:']==='gzip'){
static $lim_mem;
if(!$lim_mem) $lim_mem = ini_get('memory_limit')<<20;
$slupd = unpack('L',substr($d,-4));
@$t = ($slupd[1]<$lim_mem)?gzinflate(substr($d,10),$slupd[1]):'';
if(strlen($t)) return $t;
}
@$d=gzinflate($d);
}
return $d;
}