проблема с чтением из сокета

cybexx

Guest
проблема с чтением из сокета

После открытия сокета, читаю заголовок, из которого извлекаю сколько байт будет послано после заголовка в самом сообщении (xml-данные).
Проблема в следущем: 1. не могу прочитать сразу все данные.
2. не выходит из цикла после конца сообщения
Код:
($len - длина сообщения, $fp-указатель на открытый сокет)
PHP:
while (!feof($fp)) {
     $str=fread ($fp,$len);
     fwrite($f,$str);
     $lstr=strlen($str);
             echo "прочитано - ".$lstr."<br>";
     $len=$len-$lstr;
             echo "осталось - ".$len."<br>";
                   }
Вот что имею:
PHP:
$len = 93750
================================
прочитано - 9552
осталось - 84198
прочитано - 4948
осталось - 79250
прочитано - 2920
осталось - 76330
прочитано - 1460
осталось - 74870
прочитано - 1460
осталось - 73410
прочитано - 1460
осталось - 71950
прочитано - 1460
...
осталось - 3330
прочитано - 1460
осталось - 1870
прочитано - 1460
осталось - 410
прочитано - 410
осталось - 0
прочитано - 0
осталось - 0
прочитано - 0
осталось - 0
прочитано - 0
осталось - 0
прочитано - 0
осталось - 0
...
 

Profic

just Profic (PHP5 BetaTeam)
Текс, а теперь приводим реальный код, который не работает, со всем подробностями вида открытия сокета и т.п.
ЗЫ. Этот код никак с выводом не ассоцируется...

О похоже у него сокет зачем-то открыт в non-blocking режиме, и открывает он видимо соединение, которое не закрывается после чтения данных в ожидании посылки, видимо, новых комманд. cybexx, так?
 

cybexx

Guest
PHP:
$fp = fsockopen ($HOST, $PORT, $errno, $errstr);
if (!$fp)
    { echo "$errstr ($errno)<br>\n";
      return;
    }
else
    { 
      fputs ($fp, $str);    // отправляем заголовок
      fputs ($fp, $querry); // отправляем запрос
      $str=fgets ($fp,101);  // получаем заголовок 100 байт  
      $len=get_field($str,0,4);  // извлекаем длину ответа     
      echo $len;

     while (!feof($fp)) { 
     $str=fread ($fp,$len); 
     fwrite($f,$str); 
     $lstr=strlen($str); 
             echo "прочитано - ".$lstr."<br>"; 
     $len=$len-$lstr; 
             echo "осталось - ".$len."<br>"; 
                   }
    fclose ($fp);

    }
Закрываю соединение после выхода из цикла, а из цикла не выходит.
И еще: обратили внимание, что считывается почему-то по 1460 байт?
 

SiMM

Новичок
Молодец.... цена приведённому тобой коду - ноль. Ты не показал ничего нового из того, что нужно, и чего не было в первом скрипте. Тут, между прочим, не телепаты и не гадалки...
 

cybexx

Guest
Что именно-то интересует ?
Вырезал только лишнее. Или прислать как есть?
 

cybexx

Guest
Уверен, что дело не в хидерах, но смотри, тебе виднее ...
Поясню: вот что отсылается в сокет:

fputs ($fp, $str); // отправляем заголовок, длиной ровно 100 байт, содержит служебную инфу(идентификатор запроса, id клиента, длину самого запроса)
fputs ($fp, $querry); // отправляем запрос (xml-документ)

больше ничего не отправляю, потом только читаю из сокета
PHP:
//	function get_fsocket($fp,$len)
// читает из сокета $fp $len байт и записывает их в файл $fname
function get_fsocket($fp,$len,$fname)
{
	$f=fopen($fname,"a+") or die ("ERROR open file!!!");
  	ftruncate($f,0);
    echo "================================<br>";
echo '$len = '.$len."<br>";
    echo "================================<br>";
	while (!feof($fp)) {

		    $str=fread ($fp,$len);
		    fwrite($f,$str);
		    $lstr=strlen($str);
echo "прочитано - ".$lstr."<br>";
            $len=$len-$lstr;
echo "осталось - ".$len."<br>";
}

    fclose($f);

}
//============================================
//создается тело запроса

function init_querry(){
$querry='<?xml version="1.0" encoding="cp1251"?>
<sirena>  <query>    <describe>      <data>city</data></describe>  </query></sirena>';
return ($querry);
}

function form_header ($querry,$id_querry,$id_client)
{
	$len_querry=strlen($querry);
	$time_init_querry=time(); // дата создания запроса
//== формируем заголовок ============================================
	$str=form_field_of_header($len_querry,4);             // Длина текста сообщения
	$str.=form_field_of_header($time_init_querry,4);      // Время создания запроса
	$str.=form_field_of_header($id_querry,4);             // Идентификатор сообщения
	$str.=form_field_of_header(0,32);                     //
	$str.=form_field_of_header($id_client,2);             // Идентификатор клиента
	$str.=form_field_of_header(0,54);                     //
    return($str);
}
//==================================
//=  BEGIN  ========================
$querry=init_querry(); // формируем запрос
$id_querry=get_id_querry(); // получаем идентификатор запроса
$str=form_header ($querry,$id_querry,$id_client);
//===========================================
$fp = fsockopen ($HOST, $PORT, $errno, $errstr);
if (!$fp)
    { echo "$errstr ($errno)<br>\n";
      return;
    }
else
    { echo "Socket open . ".$fp."<br>";
      echo "Send query ... <br>";
      fputs ($fp, $str);    // отправляем заголовок
      fputs ($fp, $querry); // отправляем запрос
      echo "Reseive answer ... <br>";
      $str=fgets ($fp,101);  // получаем заголовок
      $len=get_field($str,0,4);  // извлекаем длину ответа

      get_fsocket($fp,$len,$answer_body);

      fclose ($fp);
								  echo "Socket close .<br>";
    }
 

Profic

just Profic (PHP5 BetaTeam)
Тут явно подземный стук (кстати эту пелену кода даже не читал)
но кто тебе мешает использовать цикл по $len, коли она тебе известна?
PHP:
while ($len) {
...
}
Или даже так
PHP:
while ($slen = strlen ($str = fread ($fp, $len))) {
...
}
 

cybexx

Guest
пробовал такой вариант:
PHP:
    do  {
	    $str=fread ($fp,$len);
	    fwrite($f,$str);
	    $lstr=strlen($str);
echo "прочитано - ".$lstr."<br>";
            $len=$len-$lstr;
echo "осталось - ".$len."<br>";
        } while ($len<>1);
Но вопрос то в том почему
PHP:
$str=fread ($fp,$len);
сразу все не читает?
PS что за подземный стук?
а "эту пелену кода" сам просил.
 

Profic

just Profic (PHP5 BetaTeam)
cybexx
Это был уже не я :) SIMM видимо не сообразил, что запрос идет не на http сервер.
А вот почему fread не читает сразу все и есть подземный стук.
Объяснение видимо таиться в
Reading stops when length bytes have been read, EOF (end of file) is reached, or (for network streams) when a packet becomes available, whichever comes first.
Но что это означает я, честно говоря не понимаю. Вернее как чтение может прекратиться в тот момент когда появляется пакет. Либо я уже туплю, либо что-то не так с мануалом.

А насчет твоего второго цикла - там опять же условие выхода из цикла неправильное
 

cybexx

Guest
Автор оригинала: Profic
cybexx
Это был уже не я :)
Извини :)
Получается, что этот корявый вариант - единственный ?

-~{}~ 16.11.04 01:00:

А насчет твоего второго цикла - там опять же условие выхода из цикла неправильное
Вот так вроде работает.
PHP:
  do  { 
        ...
        } while ($len<>0);
 

Profic

just Profic (PHP5 BetaTeam)
cybexx
Почему корявый? (Это я про
PHP:
while ($slen = strlen ($str = fread ($fp, $len)))
). Он конечно на первый взгляд кажется страшным, но я так (почти) читаю из сокетов, чтобы не париться почему там чего-то не пришло, а тут, наоборот, пришло больше чем нужно :)

Т.е. у меня чтение из сокета происходит примерно так
PHP:
$str = '';
while (strlen ($buf = fread ($fp, 1024))) {
    $str .= $buf;
}
-~{}~ 16.11.04 01:11:

Почему неправильное? Вроде работает.
Дык у тебя сравнение идет с 1, но длина в концке концов то равна нулю. Мне даже странно как оно работает :)
 

cybexx

Guest
Твой вариант у меня выполняется гораздо дольше
PHP:
$str = '';
while (strlen ($buf = fread ($fp, 1024))) {
    $str .= $buf;
}
Socket open . Resource id #5
Send query ...
Reseive answer ...
================================
$len = 93750
================================
Socket close .
Время выполнения: 61.0090360641 сек.

PHP:
do{
 $str=fread ($fp,$len);
 fwrite($f,$str);
 $lstr=strlen($str);
 $len=$len-$lstr;
  } while ($len<>0);
Socket open . Resource id #5
Send query ...
Reseive answer ...
================================
$len = 93750
================================
Socket close .

Время выполнения: 0.825997114182 сек.
 

Profic

just Profic (PHP5 BetaTeam)
Хех, похоже, что все же сервак с другой стороны немного тупит.
Остается только вариант с проверкой длины в условии цикла.
Кстати, проверять на <> 0 нет смысла, т.к. это и так сделает сам php, т.е. вместо "while ($len<>0);" достаточно просто написать "while ($len);" :)
 

Screjet

Новичок
Считывай с заголовка размер передаваемого тебе пакета и читай ровно столько же байт. Вероятно сервер работает в Keep-alive и ожидает от тебя новый запрос данных.
 
Сверху