Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting time interval in HTML5 server sent events

I want to send regular updates from server to client. For that I used server-sent event. I'm pasting the codes below:

Client side

Getting server updates

<script>
if(typeof(EventSource)!="undefined")
{
   var source=new EventSource("demo_see.php");
   source.onmessage=function(event)
   {
      document.getElementById("result").innerHTML=event.data + "<br>";
   }
}
else
{
   document.getElementById("result").innerHTML="Sorry, your browser does not support    server-sent events...";
}
</script>
</body>
</html>

Server side

<?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    $x=rand(0,1000);
    echo "data:{$x}\n\n";
    flush();
?>

The code works fine but it sends updates in every 3 seconds. I want to send updates in milliseconds. I tried sleep(1) after flush() but it only increases the interval further by 1 sec. Does anyone have an Idea how I can accomplish this?

Also, can I send images using server-sent events?

like image 453
ndh Avatar asked May 01 '13 08:05

ndh


2 Answers

As discussed in the comments above running a PHP script in an infinite loop with a sleep or a usleep is incorrect for two reasons

  • The browser will not see any event data (presumably it waits for the connection to close first) while that script is still running. I recall that early browser implementations of SSE allowed this but it is no longer the case.
  • Even if it did work browser-side you would still be faced with the issue of having a PHP script that runs excessively long (until the PHP.ini time_out settings kick in). If this happens once or twice it is OK. If there are X thousand browsers that simultaneously seek the same SSE from your server it will bring down your server.

The right way to do things is to get your PHP script to respond with event stream data and then gracefully terminate as it would normally do. Provide a retry value - in milliseconds - if you want to control when the browser tries again. Here is some sample code

function yourEventData(&$retry)
{
 //do your own stuff here and return your event data.
 //You might want to return a $retry value (milliseconds)
 //so the browser knows when to try again (not the default 3000 ms)
}

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Access-Control-Allow-Origin: *');//optional

$data = yourEventData($retry);

echo "data:{$str}\n\nretry:{$retry}\n\n";

As an answer to the original question this is a bit late but nevertheless in the interests of completeness:

What you get when you poll the server in this way is just data. What you do with it afterwards is entirely up to you. If you want to treat those data as an image and update an image displayed in your web page you would simply do

document.getElementById("imageID").src = "data:image/png;base64," + Your event stream data;

So much for the principles. I have on occasion forgotten that retry has to been in milliseconds and ended up returning, for example, retry:5\n\n which, much to my surprise, still worked. However, I would hesitate to use SSE to update a browser side image at 100ms intervals. A more typical usage would be along the following lines

  • User requests a job on the server. That job either gets queued behind other jobs or is likely to take quite a bit of time to execute (e.g. creating a PDF or an Excel spreadsheet and sending it back)
  • Instead of making the user wait with no feedback - and risking a timeout - one can fire up an SSE which tells the browser the ETA for the job to finish and a retry value is setup so the browser knows when to look again for a result.
  • The ETA is used to provide the user with some feedback
  • At the end of the ETA the browser will look again (browsers do this automatically so you need do nothing)
  • If for some reason the job is not completed by the server it should indicate that in the event stream it returns, e.g. data{"code":-1}\n\n so browser side code can deal with the situation gracefully.

There are other usage scenarios - updating stock quotes, news headlines etc. Updating images at 100ms intervals feels -a purely personal view - like a misuse of the technology.


It is now close to 5 years since I posted this answer and it still gets upvoted quite regularly. For the benefit of anyone still using it as a reference - in many ways SSE is, in my view, a rather outdated technology. With the advent of widespread support for WebSockets why bother doing SSE. Quite apart from anything else the cost of setting up and tearing down an HTTPS connection from the browser for each browser side retry is very high. The WSS protocol is far more efficient.

A spot of reading if you want to implement websockets

  1. Client Side
  2. Server side via PHP with Ratchet
  3. With Nginx and NChan

To my mind PHP is not a great language to handle websockets and Ratchet is far from easy to setup. The Nginx/NChan route is far easier.

like image 75
DroidOS Avatar answered Sep 26 '22 08:09

DroidOS


The reason for this behavior (message every 3 seconds) is explained here:

The browser attempts to reconnect to the source roughly 3 seconds after each connection is closed

So one way to get message every 100 milliseconds is changing the reconnect time: (in the PHP)

echo "retry: 100\n\n";

This is not very elegant though, better approach would be endless loop in PHP that will sleep for 100 milliseconds on each iteration. There is good example here, just changing the sleep() to usleep() to support milliseconds:

while (1) {
    $x=rand(0,1000);
    echo "data:{$x}\n\n";
    flush();
    usleep(100000); //1000000 = 1 seconds
}
like image 25
Shadow Wizard Hates Omicron Avatar answered Sep 26 '22 08:09

Shadow Wizard Hates Omicron