Решение в конце вопроса
Я пишу 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: Hangs видели до 40 секунд. Кажется, что он не фиксирован на 10 секунд, но это странно кажется приятным круглым числом каждый раз.
Решение (благодаря chathux) :
stream_get_contents($stream, $bytes)
будет блокироваться до тех пор, пока он не получит $bytes
байтах $bytes
или истечет время ожидания. Это означает, что мой код доходил до конца и пытался прочитать 5 байтов (которых не было), поэтому он ожидал 10 секунд, прежде чем сдаться.
Поскольку я знаю, что минимальный размер сообщения, возвращающегося ко мне, составляет 49 байт, я сначала прочитал эти 49 байтов (блокировка до тех пор, пока я не получу их или не истечет 10 лет), чтобы заполнить поле stream_get_meta_data
unread_bytes
. После этого я динамически настраиваю размер буфера на min(16*1024, unread_bytes)
поэтому я либо читаю 16k за один раз, либо все оставшиеся байты, в зависимости от того, что меньше. В моем случае это обычно означает только, что два проходят через цикл, поскольку сообщения часто крошечные (49 байтов + полезная нагрузка).
Система теперь висит примерно на 3 секунды вместо 10, но она зависает, ожидая появления первых нескольких байтов (а не в конце), которые могут быть отложены для задержки в сети и других нормальных факторов.
документация говорит: «stream_get_contents () работает с уже открытым ресурсом потока и возвращает оставшееся содержимое в строке, вплоть до maxlength байт и начиная с указанного смещения».
поэтому, когда вы предоставляете 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
аут устанавливается для самого потока, а не для каждого stream_get_contents