HTML5 Server-Sent Events prototyping - ambiguous error and repeated polling?

tomfumb picture tomfumb · Jan 30, 2012 · Viewed 12.9k times · Source

I'm trying to get to grips with Server-Side Events as they fit my requirements perfectly and seem like they should be simple to implement, however I can't get past a vague error and what looks like the connection repeatedly being closed and re-opened. Everything I have tried is based on this and other tutorials.

The PHP is a single script:

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}

$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
?>

and the JavaScript looks like this (run on body load):

function init() {

    var source;
    if (!!window.EventSource) {
        source = new EventSource('events.php');
        source.addEventListener('message', function(e) {
            document.getElementById('output').innerHTML += e.data + '<br />';
        }, false);
        source.addEventListener('open', function(e) {
            document.getElementById('output').innerHTML += 'connection opened<br />';
        }, false);
        source.addEventListener('error', function(e) {
            document.getElementById('output').innerHTML += 'error<br />';
        }, false);
    }
    else {
        alert("Browser doesn't support Server-Sent Events");
    }
}

I have searched around a bit but can't find information on

  1. If Apache needs any special configuration to support server-sent events, and
  2. How I can initiate a push from the server with this kind of setup (e.g. can I simply execute the PHP script from CLI to give a push to the already-connected-browser?)

If I run this JS in Chrome (16.0.912.77) it opens the connection, receives the time, then errors (with no useful information in the error object), then reconnects in 3 seconds and goes through the same process. In Firefox (10.0) I get the same behaviour.

EDIT 1: I thought the issue could be related to the server I was using, so I tested on a vanilla XAMPP install and the same error comes up. Should a basic server configuration be able to handle this without modification / extra configuration?

EDIT 2: The following is an example of output from the browser:

connection opened
server time: 01:47:20
error
connection opened
server time: 01:47:23
error
connection opened
server time: 01:47:26
error

Can anyone tell me where this is going wrong? The tutorials I have seen make it look like SSE is very straightforward. Also any answers to my two numbered questions above would be really helpful.

Thanks.

Answer

Faison Zutavern picture Faison Zutavern · Feb 20, 2012

The problem is your php.

With the way your php script is written, only one message is sent per execution. That's how it works if you access the php file directly, and that's how it works if you access the file with an EventSource. So in order to make your php script send multiple messages, you need a loop.

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
while(true) {
  $serverTime = time();
  sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
  sleep(1);
}
?>

I have altered your code to include an infinite loop that waits 1 second after every message sent (following an example found here: Using server-sent events).

This type of loop is what I'm currently using and it eliminated the constant connection drop and reconnect every 3 seconds. However (and I've only tested this in chrome), the connections are now only kept alive for 30 seconds. I will be continuing to figure out why this is the case and I'll post a solution when I find one, but until then this should at least get you closer to your goal.

Hope that helps,

Edit:

In order to keep the connection open for ridiculously long times with php, you need to set the max_execution_time (Thanks to tomfumb for this). This can be accomplished in at least three ways:

  1. If you can alter your php.ini, change the value for "max_execution_time." This will allow all of your scripts to run for the time you specify though.
  2. In the script you wish to run for a long time, use the function ini_set(key, value), where key is 'max_execution_time' and value is the time in seconds you wish your script to run for.
  3. In the script you wish to run for a long time, use the function set_time_limit(n) where n is the number of seconds that you wish your script to run.