php stream_get_contents висит в конце потока
Решение в конце вопроса
Я пишу приложение PHP, которое отправляет сообщение на сервер, а затем читает ответ обратно с помощью stream_get_contents
, Я общаюсь с тем же сервером в приложении для Android таким же образом. Приложение для Android работает нормально и быстро реагирует, однако PHP зависает при чтении ответа с сервера.
В приведенном ниже примере кода я установил небольшой размер буфера в 5 байт для проверки теории. Если я удаляю этот размер буфера, он зависает, однако с размером 5 байт он висит только на последнем проходе цикла:
stream_set_timeout($this->socket, 10); //10 seconds read timeout
while (!feof($this->socket)) {
$breakOut = false;
echo 'Reading response'.time().'<br/>';
$data = stream_get_contents($this->socket, 5);
echo 'Read response'.time().'<br/>';
if ($data === false) {
$this->latestErrStr = "Timed out waiting for a response.";
return false;
} else {
$index = strpos($data, chr(3));
if ($index !== FALSE){
$breakOut = true;
$data = substr($data, 0, $index);
}
$response .= $data;
}
$stream_meta_data = stream_get_meta_data($this->socket);
//If we have no EOF marker then break if there are no bytes left to read
if($breakOut || $stream_meta_data['unread_bytes'] <= 0) {
break;
}
}
Вывод следующий:
Reading response1387463602
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463623
Как вы можете видеть, между последними двумя строками задержка составляет 10 с, но между остальными нет заметной задержки.
Также для вашей информации я использую маркер ETX (3), чтобы обозначить конец сообщения, поэтому я также останавливаюсь, если я нажму на это, а не только на конец потока.
Я делаю что-то неправильно? Есть ли лучший способ сделать это?
Заранее спасибо...
Изменить: просто чтобы быть ясно, приведенный выше код ожидает только один ответ на сообщение. Он не заботится о каких-либо данных, которые возвращаются после получения байта ETX.
Edit2: зависания были видны до 40 секунд сейчас. Кажется, он не фиксируется на 10 секундах, но странно, что каждый раз это хорошие круглые числа.
Решение (благодаря Chathux):
stream_get_contents($stream, $bytes)
будет блокировать, пока не получит $bytes
байты или время ожидания истекло. Это означает, что мой код достиг конца и пытался прочитать 5 байтов (которых не было), поэтому он ждал 10 секунд, прежде чем сдаться.
Поскольку я знаю, что минимальный размер сообщения, возвращающегося мне, составляет 49 байт, я сначала читаю эти 49 байт (блокировка, пока я не получу их или не истечет 10 с), чтобы заполнить stream_get_meta_data
"s unread_bytes
поле. После этого я динамически настраиваю размер буфера min(16*1024, unread_bytes)
поэтому я либо читаю 16k за раз, либо все оставшиеся байты, в зависимости от того, что меньше. В моем случае это обычно означает, что два цикла проходят, так как сообщения часто крошечные (49 байт + полезная нагрузка).
Теперь система зависает примерно на 3 секунды вместо 10, но зависает в ожидании поступления первых нескольких байтов (а не в конце), что может быть связано с задержкой в сети и другими обычными факторами.
2 ответа
В документации говорится, что "stream_get_contents() работает с уже открытым потоковым ресурсом и возвращает оставшееся содержимое в строке размером до макс. длины, начиная с указанного смещения".
поэтому, когда вы задаете 5 как maxlength, он будет считывать до пяти байтов и продолжится. если он не может прочитать до 5 байт, он будет ждать и истечет через 10 секунд, которые вы упомянули в stream_set_timeout
пример:
//server side statement<br/>
$data = stream_get_contents($this->socket, 5);
//corresponding client code<br/>
fwrite($client, "1234");
в этом случае сервер будет ждать, пока вы не напишите еще один байтfwrite($client, "5");
Я предлагаю вам просто использовать sleep($seconds)
функция или даже usleep($nanoseconds)
функция. Тайм-аут устанавливается для самого потока, а не для каждого stream_get_contents