fread при использовании fsockopen возвращает обрезанное значение

Bermuda

Новичок
fread при использовании fsockopen возвращает обрезанное значение

Мне конечно дико стыдно, но вот уже несколько часов бьюсь над отладкой пяти строк кода.

PHP:
<?
$host = "phpclub.ru";

$get = "GET / HTTP/1.0\r\n".
       "Host: " . $host . "\r\n".
       "Connection: Keep-Alive\r\n\r\n";

$fp = fsockopen($host, 80);
fwrite($fp, $get);
$res = fread($fp, 100000);
fclose($fp);
   
print $res;
?>
При выполнении скрипта $res почему-то содержит только часть информации (строка обрезается). Проверял на Win/Linux, PHP4/PHP5, на разных url.

Что я делаю не так?
 

Bermuda

Новичок
Автор оригинала: SiMM
[m]fread[/m]
Спасибо. Я невнимательно читал документацию.

Внимание
При чтении из сетевых потоков или конвееров, таких как те, что возвращаются при чтении удаленных файлов или из popen() и fsockopen(), чтение остановится после того, как пакет станет доступным. Это означает, что вы должны собирать данные вместе по кусочкам, как показано на примере ниже.

PHP:
<?php
$handle = fopen("http://www.example.com/", "rb");
$contents = '';
while (!feof($handle)) {
  $contents .= fread($handle, 8192);
}
fclose($handle);
?>
В итоге заработало в такой модификации.

PHP:
<?php
$host = "phpclub.ru";

$get = "GET / HTTP/1.0\r\n".
        "Host: " . $host . "\r\n".
        "Connection: Keep-Alive\r\n\r\n";

$fp = fsockopen($host, 80);
fwrite($fp, $get);
while (!feof($fp)) {
  $res .= fread($fp, 1024);
}

fclose($fp);
   
print $res;

?>
Вообще говоря, довольно неочевидное поведение функции fread()
 

ForJest

- свежая кровь
Bermuda
Поведение вполне логичное - данные накапливаются в буфере и потом отдаются тебе по мере поступления.
Так ты можешь прекратить загрузку в любой момент.
 

Bermuda

Новичок
Автор оригинала: ForJest
Bermuda
Поведение вполне логичное - данные накапливаются в буфере и потом отдаются тебе по мере поступления.
Это смотря с какой точки зрения посмотреть. С точки зрения отдающего севрера конечно же логично, а ч точки зрения функции fread -- никак нет. Когда я делаю fread я прошу у php "дай мне с такого-то ресурса, столько-то байт", и функция должна вернуть столько байт сколько я попросил, но не более чем содержится в ресурсе. Но вместо этого она мне возвращает столько, сколько смогла за один раз, а чтобы получить что там еще осталось, нужно просить ее еще раз. Это все равно что я попрошу у тебя яблоко, а ты мне будешь отдавать его по кускам. Но я ведь не просил его резать. Не можешь передать целое, так и скажи. А еще лучше, в данной ситуации иметь отдельную функцию. Хуже всего то, что поведение функции меняется от типа ресрса, чего не должно быть. Это должно быть прозрачно для конечного пользователя.
 

kruglov

Новичок
Поведение fread от типа ресурса не меняется. Он и локальные файлы так же кусочками читает, например.
 

SiMM

Новичок
kruglov, по моему, вы не поняли. Ни что не мешает сделать
PHP:
$fp = fopen($path,'rb'); 
$data = fread($fp, 100000);
чтобы получить в $data не менее 100000 байт содержимого файла, если файл не короче 100000. Тот же фокус с сокетом или с удалённым ресурсом ($path=$url), как продемонстрировал Bermuda - не проходит.
 

Bermuda

Новичок
Но самое интересное, непонятно какого размера должны быть кусочки для чтения. Я пробовал на разных серверах -- везде они разные. Т. е. если читать кусками большими чем отдает сервер, то fread прекратит свою работу. Следовательно нужно брать минимальный размер, т. е. 1 байт? Более того, если сервер еще не готов отдать очередную порция, то вот этот кусок
PHP:
while (!feof($fp)) { 
  $res .= fread($fp, 1024); 
}
будет гонять ПУСТОЙ цикл пока не получит !feof == true, а это расточительство процессора, можно делать sleep, но тоже непонятно на какое время, да и некрасиво это.

Смешно как-то :)

Вот пример того, что сервера возвращают куски разного размера, однако пустого цикла у меня не получилось, но думаю, что при определенных условиях можно и это доказать.

PHP:
<?php 
$host = "phpclub.ru"; 

$get = "GET / HTTP/1.0\r\n". 
        "Host: " . $host . "\r\n". 
        "Connection: Keep-Alive\r\n\r\n"; 

$fp = fsockopen($host, 80); 
fwrite($fp, $get); 
while (!feof($fp)) { 
  $res = "";
  $res = fread($fp, 10000000);
  print strlen($res)."\n";
} 

fclose($fp); 
    
?>
Yandex.ru
1538
1380
1448
1448
1334
1448
1448
1448
1296
1448
1448
1448
1448
1258
1448
1448
1448
1266
0

phpclub.ru
1538
1456
1448
1448
1448
1448
1448
1448
1448
1448
1448
1448
1448
1448
1448
1448
1448
1448
1448
1004
0

Localhost
4380
6724
0
 

kruglov

Новичок
sleep не надо делать, fread ждет, пока не прочтет хотя бы байт.

-~{}~ 07.10.05 15:51:

вернее не прочтет пакет, а пакетов по 0 байт не бывает.
 

ForJest

- свежая кровь
SiMM
Давай не вступать в бесполезный спор. В документации чётко описано:
[m]fread[/m]
fread() reads up to length bytes from the file pointer referenced by handle. 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.
Никто не общает, заметь, что будет возвращено именно length байт. Просто гарантируется, что будет _не больше_ чем length байт :).

Ну и кстати твоё заявление остановилось на 10000 байт. Попробуй почитать гиговый файл кусками по 8Мб - полагаю что поведение будет похожим :). Хотя я и не проверял.

-~{}~ 07.10.05 20:46:

Bermuda
Снова невнимательно прочёл документацию? :)
(для сетевых потоков) когда пакет становится доступным, что бы не произошло первым.
 

SiMM

Новичок
> Давай не вступать в бесполезный спор.
Разве я с кем-то спорил? :)
 

Bermuda

Новичок
ForJest, том-то и странность, что для "для сетевых потоков" функция делает некое исключение. Я считаю, что немного нелогично менять принцип работы в зависимости от типа ресурса. Вот допустим я не вижу никакой разницы откуда читать -- из файла или из сетевого ресурса. Единственное что я хочу, так это получить из указанного ресурса указанное количество байт, одним вызовом функции.

Ну да ладно, работает как работает, разобрался где были грабли и ладно. Если разработчики считают такое поведение функции более целесообразным, то кто я такой чтобы спорить?
 
Сверху