HTML5 Server-Sent этакая замена AJAX

Сижу вот на природе, тихий вечер, вокруг летают комары и прочая нечесть, где-то рядом, мой кот пытается поймать пчелу.. Одним словом — весна! Давно я ждал этого момента, когда можно отбросить все дела в сторону и просто наслаждаться природой. В голове крутится фраза «блог настало твоё время!»

Так вот, решил я написать коротенькую статью о такой штуке как Server-Sent. При первом рассмотрении может показаться, что это бесполезная хрень и всеми любимый AJAX ничто не скинет с трона. Но давайте сначала разберёмся что за зверь Server-Sent? Это, ничто иное как модель событий HTML5 позволяющая получить актуальное обновление от сервера в браузере. В этой маленькой статье я опишу как управлять полученными данными посредством объекта EventSource и выдавать их на странице.

При использовании модели AJAX, наш код может непрерывно отсылать запросы на сервер для получения новых или обновлённых данных, при этом, запросить эту информацию должен пользователь. Использование запросов Server-Sent позволяет «транслировать» данные с сервера, тем самым постоянно обновляя их необходимости участия пользователя в этом процессе. С момента когда на странице запускается событие Server-Sent, сервер начинает отправлять поток изменённых или новых данных. А простенький javascript-код может обрабатывать полученные данные и сразу же вставлять их в страницу.

Создание простой HTML5 страницы

Для того чтобы всё стало понятнее, напишем маленькое приложение отображающее произвольную информацию с сервера без необходимости обновлять страницу. Ну а для начала напишем саму страничку и простенький javascript-код для получения и отображения данных. Выглядеть всё будет примерно так:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="serverData">Данные тут</div>
</body>
</html>

Тут, в простой разметке страницы, никаких вопросов быть не должно, как впрочем и в следующем javascript-коде:

<script type="text/javascript">
if(typeof(EventSource)!=="undefined") {
	//создаем объект, и указываем где лежит серверная часть обработчика
	var eSource = new EventSource("send_sse.php");
	//ждем сообщения с данными
	eSource.onmessage = function(event) {
		//запись обновлённых данных на страницу
		document.getElementById("serverData").innerHTML = event.data;
	};
}
else {
	document.getElementById("serverData").innerHTML="Ошибочка! Твой браузер не поддерживает server-sent события.";
}
</script>

Создание скрипта на серверной стороне

Теперь, напишем элементарный код для серверной части. Вся его суть будет заключаться в генерировании случайного числа. Единственное что необходимо обязательно сделать, это предотвратить возможные проблемы с кешированием путём добавления в заголовок обработчика теги запрета кеширования. Правда я как-то страшно это всё описал легче просто посмотреть на пример:

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
//генерация числа
$new_data = rand(0, 1000);
//вывод числа
echo "data: Новое число: $new_data";
flush();
?>

Вот собственно и всё, можете сохранить этот код у себя на сервере и проверить. Всё должно работать.

Кстати, если вы обратили внимание на «весеннее» начало, поясню, статью начинал писать около месяца назад, когда погода была, намного прекраснее той что за окном сейчас, как это не странно =) Всем удачи, пишите комментарии, подписывайтесь на Твиттер, кликайте по кнопкам и улыбайтесь!

Комментарии моих читателей

  1. Вячеслав сказал:

    не работает куда явускрипт вставлять то?!

    • Вячеслав сказал:

      в общем в гуглхроме не работает опера пашет)))

      • Хромом не пользуюсь, но вообще там тоже работать должно..

        • Саня сказал:

          Как заставить его транслировать данные из БД?? Например есть таблица в бд, в которую с помощью разных юзверей сайта добавляются строки, как в блок «serverData» вставлять новые появившиеся строки в таблице?

          • Да как обычно, с помощью запроса к базе написанного на php. Разместить его надо в файле send_sse.php из примера, там вообще можно разместить очень много чего.

          • Саня сказал:

            Ну так запрос выполниться разово? Чтоб каждый раз он выполнялся надо в JS коде использовать setInterval(); отправляя обработчику send_sse.php последний извлеченный ID??? Как обойтись без setInterval()???

          • Просто напиши цикл на php который проверяет поступление в базу новых записей с удобной тебе задержкой и помести его в send_sse.php. Как только цикл будет находить и выводить новую запись она будет появляться на сайте.

            Можно ещё придумать подобие триггера, чтобы меньше грузить сервер, но это уже на твой выбор.

          • Саня сказал:

            Вот например код выводит число $i десять раз:
            header(‘Content-Type: text/event-stream’);
            header(‘Cache-Control: no-cache’);
            //генерация числа
            $i=0;
            for(;$i<=10;$i++)
            {
            $new_data .=$i.'’;
            //вывод числа

            }
            sleep(1);
            echo «data: $new_data»;
            flush();

            /////////////////

            Но всегда выводитятся числа от 0 до 10, а надо чтоб продолжало: 0 до 10, потом от 10 до 20 и т.д.

          • Сергей сказал:

            Ссылка которую вы ему дали содержит информацию о циклах, юзер «Саня» в чем-то тут прав, я сам попробовал вставить sleep() и ничего не вышло…..выводит криво… Возможно, правильный его вопрос, в понятливом для вас виде будет выглядеть так: «Как сделать так, чтобы цикл выводил, например по десять чисел ($i-тых), но при этом не начинал каждый раз с 0 до 10ти, а всё время продолжал суммировать и выводить число $i», т.е. получится такой вывод в браузер:

            0
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10

            //тут пауза 5сек, и далее

            11
            12
            13
            14
            15
            16
            17
            18
            19
            20

            //опять пауза 5 сек

            21
            22
            23
            24….. и так далее до бесконечности пока пользователь не закроет страницу……

          • Допустим число хранится в базе. Алгоритм такой расположенный в файле see такой:

            1. получаем число из базы.
            2. записываем в базу номер запроса (отдельное число);
            3. плюсуем число. $new_data++; и номер запроса $zapross++;
            —- проверяем, если это 10 запрос
            —- если да ставим паузу sleep(5); и обнуляем число запросов в базе.
            4. выводим число. echo «data: Новое число: $new_data»;

          • Саня сказал:

            Я погуглил, и нарыл решение того чего я хотел:

            <?php
            header('Content-Type: text/event-stream');
            header('Cache-Control: no-cache');
            header('Access-Control-Allow-Origin: ' . @$_SERVER['HTTP_ORIGIN']);

            // prevent bufferring
            if (function_exists('apache_setenv')) {
            @apache_setenv('no-gzip', 1);
            }
            @ini_set('zlib.output_compression', 0);
            @ini_set('implicit_flush', 1);
            for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
            ob_implicit_flush(1);

            // event-stream
            for ($x = 0; ; $x++) {
            echo "data: $x\n\n»;
            sleep(1);
            }
            ?>

            В этом примере выводится каждую секунду, число в браузер, всё работает. Но есть минусы:
            1) Время выполнения цикла ограничено временем выполнения скрипта как я заметил (у меня в php.ini стоит 30 сек – max_execution_time)
            2) Пример работает только в опере

            Вот теперь бы кто ответил бы на вопрос: Как обойти эти минусы???

          • Сергей сказал:

            Так вот будет работать:
            <?php
            header('Content-Type: text/event-stream');
            header('Cache-Control: no-cache');

            ini_set('max_execution_time',0);

            ob_implicit_flush();

            // event-stream
            for ($x = 0; ; $x++) {
            echo "data: $x\n\n»;
            sleep(1);
            }
            ?>

            но с реальным хостером будут проблемы

Добавить комментарий для Вячеслав Отменить ответ