Я начал использовать push в HTML5 с помощью объекта JavaScript EventSource. Я был полностью доволен рабочим решением в PHP:
$time = 0; while(true) { if(connection_status() != CONNECTION_NORMAL) { mysql_close() break; } $result = mysql_query("SELECT `id` FROM `table` WHERE UNIX_TIMESTAMP(`lastUpdate`) > '".$time."'"); while($row = mysql_fetch_array($result)) { echo "data:".$row["id"].PHP_EOL; echo PHP_EOL; ob_flush(); flush(); } $time = time(); sleep(1); }
Но внезапно мой WebApp больше не был доступен с ошибкой MySQL «слишком много соединений».
Оказалось, что соединение MySQL не закрывается после закрытия источника события в JavaScript:
window.onload = function() { sse = new EventSource("push.php"); sse.onmessage = function(event) { console.log(event.data.split(":")[1]); } } window.onbeforeunload = function() { sse.close(); }
Поэтому я предполагаю, что скрипт PHP не перестает выполняться. Есть ли способ вызвать функцию (например, die();
) до того, как соединение клиента отключится? Почему мой скрипт не заканчивается после вызова .close();
на EventSource ?!
Спасибо за помощь! –
Во-первых: когда вы завершаете свой код в огромном цикле while(true)
, ваш скрипт никогда не завершится. Связь с БД была бы закрыта, когда ваш скрипт «закончил выполнение кода» , но поскольку вы написали тупик, это не произойдет … когда-либо.
Это не проблема EventSource
, которая просто делает то, что она должна делать. Это честно, по-настоящему, и, к сожалению, ваша вина.
Дело в том, что пользователь подключается к вашему сайту и EventSource
экземпляр объекта EventSource. Установлено соединение с сервером и запрашивается запрос на возвращаемое значение push.php
. Ваш сервер делает по запросу и запускает скрипт, что -again- это не что иное, как тупик. Ошибок нет, поэтому он может продолжать работать до тех пор, пока php.ini позволяет ему работать. Метод .close()
отменяет поток вывода (или, скорее, он должен), но ваш сценарий настолько занят, либо выполняет свой бесконечный цикл, либо спящий. Предполагая, что сценарий будет остановлен, потому что клиент отключается, это похоже на то, что любой клиент может помешать тому, что делает сервер. С точки зрения безопасности это будет худшее, что может произойти. Теперь, чтобы ответить на ваш реальный вопрос: что вызывает проблему, как ее исправить?
Ответ: HEADERS
Посмотрите на этот небольшой пример PHP : сценарий не поддерживается на сервере (бесконечными циклами), но устанавливая правильные заголовки.
Точно так же, как примечание: Пожалуйста, mysql_*
ASAP, он устарел (наконец), использует PDO
или mysqli_*
istead. Честно говоря, это не так.
У меня была точно такая же проблема, и, насколько я понял, причина заключалась в том, что apache не завершал php-скрипт, пока я не попытался снова написать данные через закрытое соединение сокета.
У меня не было никаких изменений в моей базе данных, поэтому никаких результатов не было.
Чтобы исправить это, я просто повторяю комментарий источника события в моем цикле while:
echo ": heartbeat\n\n"; ob_flush(); flush();
Это приводит к завершению работы скрипта, если соединение сокета закрыто.
вы можете попытаться добавить это в цикл:
if(connection_status() != CONNECTION_NORMAL) { break; }
но php должен останавливаться, когда клиент отключается.
1.
window.onbeforeunload = function() { sse.close(); }
это не требуется, EventSource будет закрыт при разгрузке страницы.
http://www.php.net/manual/ru/function.connection-aborted.php комментарии на этой странице говорят, что u должен использовать «флеш» до connection_status в любом случае, вы должны отказаться от соединения через N секунд, потому что вы не можете обнаружить «плохие» разъединения.
3. эхо-повтор: 1000 \ n "; // используем это, чтобы сообщить EventSource о задержке пересоединения в ms
$time = 0; while(true) { if(connection_status() != CONNECTION_NORMAL) { mysql_close() break; } $result = mysql_query("SELECT id FROM table WHERE UNIX_TIMESTAMP(lastUpdate) > '".$time."'"); while($row = mysql_fetch_array($result)) { $rect[] = $row; } for($i-0;$i echo "data:".implode('',$disp)."\n\n"; $time = time(); sleep(1); }
Как отметил Элиас Ван Оотегем, ваш сценарий никогда не заканчивается, и поэтому у вас есть куча подключений MySQL. Более того, у вас есть куча ресурсов, которые используются в виде циклов PHP, работающих без конца!
К сожалению, решение о сохранении не является «заголовком». В примере по этой ссылке, на которую ссылается Elias , php-скрипт завершается, а клиентский javascript на самом деле должен повторно открыть соединение. Вы можете проверить это, используя следующий код
source.addEventListener('open', function(e) { // Connection was opened. console.log("Opening new connection"); }, false);
Если вы следуете его примеру в github, в реализации автор фактически использует цикл, который заканчивается через X секунд. См. Цикл do и комментарии внизу сценария.
Определив предел для цикла, IE X итераций или через X количество времени, чтобы вызвать конец цикла или die();
, вы сделаете так, чтобы, если клиент отключился, существует ограничение на то, как долго скрипт останется активным. Если клиент по-прежнему находится после X-го времени, он просто подключится.