Страница событий сервера PHP не загружается при использовании цикла while - PullRequest
0 голосов
/ 04 октября 2018

У меня есть файл с именем handler.php, который считывает данные из текстового файла и передает их на страницу клиента.

Соответствующий код клиента:

<script>
if(typeof(EventSource) !== "undefined") {
    var source = new EventSource("handler.php");
    source.onmessage = function(event) {
        var textarea = document.getElementById("subtitles");
        textarea.value += event.data;
        textarea.scrollTop = textarea.scrollHeight;

    };
} else {
    document.getElementById("subtitles").value = "Server-sent events not supported.";
}
</script>

Код Handler.php:

$id = 0;
$event = 'event1';
$oldValue = null;

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');

while(true){

    try {
        $data = file_get_contents('liveData.txt');
    } catch(Exception $e) {
        $data = $e->getMessage();
    }

    if ($oldValue !== $data) {
        $oldValue = $data;
        echo 'id: '    . $id++  . PHP_EOL;
        echo 'event: ' . $event . PHP_EOL;
        echo 'retry: 2000'      . PHP_EOL;
        echo 'data: '  . json_encode($data) . PHP_EOL;
        echo PHP_EOL;

        @ob_flush();
        @flush();
        sleep(1);  
        }

  }

При использовании цикла файл handler.php никогда не загружается, поэтому клиент не получает никаких данных.На вкладке «Сеть разработчиков Chrome» файл handler.php отображается как «Ожидание», а затем «Отменено».Сам файл остается заблокированным в течение 30 секунд.

Однако, если я удаляю цикл while (как показано ниже), handler.php загружается , а клиент делает получать данные (только один раз, хотя файл liveData.txt постоянно обновляется).

Handler.php без цикла:

$id = 0;
$event = 'event1';
$oldValue = null;

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');

try {
    $data = file_get_contents('liveData.txt');
} catch(Exception $e) {
    $data = $e->getMessage();
}

if ($oldValue !== $data) {
    $oldValue = $data;
    echo 'id: '    . $id++  . PHP_EOL;
    echo 'event: ' . $event . PHP_EOL;
    echo 'retry: 2000'      . PHP_EOL;
    echo 'data: '  . json_encode($data) . PHP_EOL;
    echo PHP_EOL;

    @ob_flush();
    @flush();

    }

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

Ответы [ 4 ]

0 голосов
/ 05 октября 2018

(Размещено решение от имени автора вопроса) .

Удачи!Во время отладки в девятый раз я решил вернуться к основам и начать заново.Я свернул цикл и сократил код PHP до минимума, но сохранил код на стороне клиента, предоставленный RamRaider.И теперь все работает чудесно!И играя со значением повтора, я могу точно указать, как часто данные передаются.

PHP (на стороне сервера):

 <?php

    $id = 0;
    $event = 'event1';
    $oldValue = null;

    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    header('X-Accel-Buffering: no');

    try {
        $data = file_get_contents('liveData.txt');
    } catch(Exception $e) {
        $data = $e->getMessage();
    }

    if ($oldValue !== $data) {
        $oldValue = $data;
        echo 'id: '    . $id++  . PHP_EOL;
        echo 'event: ' . $event . PHP_EOL;
        echo 'retry: 500'      . PHP_EOL;
        echo "data: {$data}\n\n";
        echo PHP_EOL;

        @ob_flush();
        @flush();

        }

  ?>

Javascript (на стороне клиента):

<script>
    if ( typeof(EventSource ) !== "undefined") {
        var url = 'handler.php'

        var source = new EventSource( url );
        var textarea = document.getElementById("subtitles");


        source.addEventListener('event1', function(e){
            textarea.value += e.data;
            textarea.scrollTop = textarea.scrollHeight;
            console.info(e.data);
        }, false );

    } else {
        document.getElementById("subtitles").value = "Server-sent events not supported.";
    }
</script>
0 голосов
/ 04 октября 2018

При использовании цикла файл handler.php никогда не загружается, поэтому клиент не получает никаких данных.На вкладке «Сеть разработчиков Chrome» файл handler.php отображается как «Ожидание», а затем «Отменено».Сам файл остается заблокированным в течение 30 секунд.

Это связано с тем, что веб-сервер (Apache), браузер или даже сам PHP отменяют запрос, если в течение 30 секунд нет ответа.

Итак, я думаю, что очистка не работает, попробуйте активно запускать и завершать буфер без использования функций @, чтобы вы получили подсказку при возникновении ошибки.

// Start output buffer
ob_start();

// Write content
echo ''; 

// Flush output buffer
ob_end_flush();
0 голосов
/ 04 октября 2018

Насколько я могу судить, клиентская сторона соединения SSE выглядит нормально - хотя я переместил var textarea..... вне обработчика onmessage.

ОБНОВЛЕНИЕ: Я должен был посмотреть ближе, но событиеконтролировать - event1, поэтому нам нужно установить прослушиватель событий для этого события.

<script>
    if( typeof( EventSource ) !== "undefined" ) {
        var url = 'handler.php'

        var source = new EventSource( url );
        var textarea = document.getElementById("subtitles");


        source.addEventListener('event1', function(e){
            textarea.value += e.data;
            textarea.scrollTop = textarea.scrollHeight;
            console.info(e.data);
        },false );

    } else {
        document.getElementById("subtitles").value = "Server-sent events not supported.";
    }
</script>

Что касается сценария сервера SSE, я склонен использовать такой метод

<?php
    /* make sure the script does not timeout */
    set_time_limit( 0 );
    ini_set('auto_detect_line_endings', 1);
    ini_set('max_execution_time', '0');

    /* start fresh */
    ob_end_clean();


    /* ultility function for sending SSE messages */
    function sse( $evtname='sse', $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";
        }
    }



    $id = 0;
    $event = 'event1';
    $oldValue = null;

    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    header('X-Accel-Buffering: no');





    while( true ){
        try {
            $data = @file_get_contents( 'liveData.txt' );
        } catch( Exception $e ) {
            $data = $e->getMessage();
        }

        if( $oldValue !== $data ) {

            /* data has changed or first iteration */
            $oldValue = $data;

            /* send the sse message */
            sse( $event, $data );

            /* make sure all buffers are cleansed */
            if( @ob_get_level() > 0 ) for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
            @flush();           
        }



        /* 
            sleep each iteration regardless of whether the data has changed or not.... 
        */
        sleep(1);
    }



    if( @ob_get_level() > 0 ) {
        for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
        @ob_end_clean();
    }
?>
0 голосов
/ 04 октября 2018

Я думаю, у вас есть проблемы с работой Интернета.Код PHP не запускается в вашем браузере - он просто создает то, что веб-сервер передает браузеру по проводам.

Как только страница загружена с сервера, вот и все.Вам нужно будет реализовать то, что опрашивает на предмет изменений.

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

Единственное другое решение - это использовать технологию push и инфраструктуру javascript, которая может выдвигать и повторно заполнять соответствующие части страницы, или цикл javascript на таймере, который извлекает данные.

...