В то время как циклы для отправленных сервером событий приводят к зависанию страницы

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

Например:

<?php
while(true) {
echo "data: This is the message.";
sleep(3);
ob_flush();
flush();
}
?>

Я ожидаю, что каждые 3 секунды «данные: это сообщение». будет выведен. Вместо этого страница просто не загружается. Однако мне нужно такое поведение для событий, отправленных сервером. Есть ли способ это исправить?

Редактировать:

Полный код:

<?php
session_start();

require "connect.php";
require "user.php";

session_write_close();

echo $data["number"];

header("Content-Type: text/event-stream\n\n");
header('Cache-Control: no-cache');

set_time_limit(1200);

$store = new StdClass(); // STORE LATEST MESSAGES TO COMPARE TO NEW ONES
$ms = 200; // REFRESH TIMING (in ms)
$go = true; // MESSAGE CHANGED

function formateNumber ($n) {
$areaCode = substr($n, 0, 3);
$part1 = substr($n, 3, 3);
$part2 = substr($n, 6, 4);
return "($areaCode) $part1-$part2";
}

function shorten ($str, $mLen, $elp) {
if (strlen($str) <= $mLen) {
return $str;
} else {
return rtrim(substr($str, 0, $mLen)) . $elp;
}
}

do {
$number = $data["number"];
$sidebarQ = "SELECT *
FROM (
SELECT *
FROM messages
WHERE deleted NOT LIKE '%$number%'
AND (
`from`='$number'
OR
`to`='$number'
)
ORDER BY `timestamp` DESC
) as mess
GROUP BY `id`
ORDER BY `timestamp` DESC";
$query = $mysqli->query($sidebarQ);

if ($query->num_rows == 0) {
echo 'data: null' . $number;
echo "\n\n";
} else {

$qr = array();
while($row = $query->fetch_assoc()) {
$qr[] = $row;
}

foreach ($qr as $c) {
$id = $c["id"];
if (!isset($store->{$id})) {
$store->{$id} = $c["messageId"];
$go = true;
} else {
if ($store->{$id} != $c["messageId"]) {
$go = true;
$store->{$id} = $c["messageId"];
}
}
}

if($go == true) {
$el = $n = "";

foreach ($qr as $rows) {
$to = $rows["to"];
$id = $rows["id"];
$choose = $to == $number ? $rows["from"] : $to;
$nameQuery = $mysqli->query("SELECT `savedname` FROM `contacts` WHERE `friend`='$choose' AND `number`='$number'");
$nameGet = $nameQuery->fetch_assoc();
$hasName = $nameQuery->num_rows == 0 ? formateNumber($choose) : $nameGet["savedname"];

$new = $mysqli->query("SELECT `id` FROM `messages` WHERE `to`='$number' AND `tostatus`='0' AND `id`='$id'")->num_rows;
if ($new > 0) {
$n = "<span class='new'>" . $new . "</span>";
}

$side = "<span style='color:#222'>" . ($to == $number ? "To you:" : "From you:") . "</span>";
$el .= "<div class='messageBox sBox" . ($nameQuery->num_rows == 0 ? " noname" : "") . "' onclick=\"GLOBAL.load($id, $choose)\" data-id='$id'><name>$hasName</name><div>$side " . shorten($rows["message"], 25, "...") . "</div>$n</div>";
}
echo 'data: '. $el;
echo "\n\n";

$go = false;
}
}

echo " ";

ob_flush();
flush();
sleep(2);
} while(true);
?>

Я также хотел бы отметить, что этот бесконечный цикл не должен вызывать этого. Именно так обычно настраиваются SSE, и это даже делается на веб-сайте MDN.

23

Решение

Без сомнения, вы уже поняли это, но в действительности вы не использовали код, подобный следующему, для пары сценариев sse, и это сработало как шарм. Приведенный ниже код является общим и не включает обработку SQL или наборов записей, но идея обоснована (!?)

<?php
set_time_limit( 0 );
ini_set('auto_detect_line_endings', 1);
ini_set('mysql.connect_timeout','7200');
ini_set('max_execution_time', '0');

date_default_timezone_set( 'Europe/London' );
ob_end_clean();
gc_enable();header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET');
header('Access-Control-Expose-Headers: X-Events');if( !function_exists('sse_message') ){
function sse_message( $evtname='chat', $data=null, $retry=1000 ){
if( !is_null( $data ) ){
echo "event:".$evtname."\r\n";
echo "retry:".$retry."\r\n";
echo "data:" . json_encode( $data, JSON_FORCE_OBJECT|JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS );
echo "\r\n\r\n";
}
}
}

$sleep=1;
$c=1;

$pdo=new dbpdo();/* wrapper class for PDO that simplifies using PDO */

while( true ){
if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) {
break;
}
/* Infinite loop is running - perform actions you need */

/* Query database */
/*
$sql='select * from `table`';
$res=$pdo->query($sql);
*/

/* Process recordset from db */
/*
$payload=array();
foreach( $res as $rs ){
$payload[]=array('message'=>$rs->message);
}
*/

/* prepare sse message */
sse_message( 'chat', array('field'=>'blah blah blah','id'=>'XYZ','payload'=>$payload ) );

/* Send output */
if( @ob_get_level() > 0 ) for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
@flush();

/* wait */
sleep( $sleep );
$c++;

if( $c % 1000 == 0 ){/* I used this whilst streaming twitter data to try to reduce memory leaks */
gc_collect_cycles();
$c=1;
}
}if( @ob_get_level() > 0 ) {
for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
@ob_end_clean();
}
?>
7

Другие решения

Хотя это не является прямым ответом на проблему, попробуйте использовать этот метод, чтобы найти ошибку. Вы не получаете ошибок, но это должно помочь вам найти их, может быть?

По сути, вы хотите иметь простой скрипт PHP, который включает в себя ваш основной скрипт, но эта страница допускает ошибки … Пример ниже ..

index.php / Simple Error Includer

<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
require "other.php";
?>

other.php / Вы главный сценарий

<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
weqwe qweqeq
qweqweqweqwe

?>

Если вы создадите такую ​​настройку, то при просмотре index.php вы увидите следующую ошибку Parse error: syntax error, unexpected 'qweqeq' (T_STRING) in /var/www/html/syntax_errors/other.php on line 5 потому что он не имеет недопустимого синтаксиса на главной странице и позволяет проверять любые включения.

Но если вы посмотрите где other.php, вы просто получите белую / пустую страницу, потому что она не может проверить всю страницу / скрипт.

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

Пожалуйста, поймите код, прежде чем комментировать
сказать, что это отключает контроль ошибок означает, что вы не удосужились RTM

Заполните буфер

Еще одна проблема в прошлом, которую я помню, — заполнение буфера до того, как он будет выведен в браузер. Так что попробуйте что-то вроде этого перед вашей петлей.

echo str_repeat("\n",4096);  // Exceed the required browser threshold
for($i=0;$i<70;$i++)     {
echo "something as normal";
flush();
sleep(1);
}

Примеры на http://www.sitepoint.com/php-streaming-output-buffering-explained/

5

Я собираюсь рискнуть и констатировать очевидное,

Вы можете запрашивать сервер каждые 3 секунды, и позволить клиенту сделать ожидание …

Это можно легко сделать с помощью JavaScript

например, попробуйте этот код и назовите, если file.php

<?php
$action='';
if (array_key_exists('action',$_GET))
{$action=$_GET['action'];}
if ($action=='poll')
{
echo "this message will be sent every 3 sec";
}
else
{
?><HTML><HEAD>
<SCRIPT SRC="http://code.jquery.com/jquery-2.1.3.min.js"></SCRIPT>
<SCRIPT>
function doPoll()
{
$('#response').append($.get("file.php?action=poll"));
setTimeout(doPoll, 3000);
}
doPoll();
</SCRIPT>
</HEAD><BODY><DIV id="response"></DIV></BODY></HTML><?php
}
4

Одна вещь, которую я заметил здесь, это sleep() функция в сочетании с ob_start() и — НЕТ — ob_start () где-нибудь в полном примере кода, но есть flush() а также ob_flush() ..

Что ты покраснешь в любом случае?
А почему не просто ob_end_flush() ?

Дело в том, что sleep() чем echo(), чем sleep() опять же, чем echo() снова и т. д. и т. д. не действует при включенной буферизации вывода. Функция ожидания работает, как ожидается, когда выходная буферизация не находится в режиме воспроизведения. На самом деле, он может * (и будет) давать совершенно неожиданные результаты, и мы не хотим видеть эти результаты.

4

Кажется, что функция сна мешает выводу. Установка функции сна ПОСЛЕ ВРЕМЕНИ сработала:

<?php
while(true) {
echo "data: This is the message.";
ob_flush();
flush();
sleep(3);
}

Как полагают другие, я бы рекомендовал использовать AJAX вместо бесконечного цикла, но это был не ваш вопрос.

4

Вместо использования цикла, попробуйте этот код, приведенный ниже, который работает (проверил себя) в соответствии с вашими требованиями

echo "data: This is the message.";
$url1="<your-page-name>.php";
header("Refresh: 5; URL=$url1");

то, что это будет делать, это будет вызывать себя каждые 5 секунд (в вашем случае установите его на 3 вместо 5) и отобразит вывод.

Надеюсь, что это решит вашу проблему

4

Может ли это быть так просто, как время ожидания скрипта?

В конечном итоге PHP-скрипты самостоятельно завершаются, если они работают слишком долго. Решение, когда вы не хотите, чтобы это происходило, состоит в том, чтобы сбрасывать время ожидания.

Так что простым дополнением может быть все, что вам нужно:

<?php
while(true) {
echo "data: This is the message.";
set_time_limit(30);
sleep(3);
ob_flush();
flush();
}
?>

Конечно, это может быть не так, но мой инстинктивный инстинкт заключается в том, что это проблема.

ОБНОВЛЕНИЕ: Я заметил в комментариях, что вы используете бесплатный хостинг. Если они работают на PHP в safe mode тогда вы не сможете сбросить время ожидания.

3

Следующий код прекрасно работает здесь, также используя Mayhem его функцию str_repeat для добавления 4k данных (что обычно является минимумом для сброса пакета tcp php)

echo str_repeat(' ', 4096);
while(true)
{
echo "data: This is the message.";
flush();
sleep(3);
}
3
По вопросам рекламы [email protected]