[Guided Reading] HPBN - SSE (Server-Sent Events)


Server-Sent Events enables efficient server-to-client streaming of text-based event data—e.g., real-time notifications or updates generated on the server.

It was an early addition to the HTML5 specification and is natively supported by most modern browsers.


  • Low latency (single, long-lived HTTP connection)
  • Efficient message parsing (no unbounded buffer)
    • Unlike a raw XHR connection, which buffers the full received response until the connection is dropped, an SSE connection can discard processed messages without accumulating all of them in memory.
  • Automatic tracking of last seen message and auto-reconnect
    • EventSource will automatically reconnect to the server and optionally advertise the ID of the last seen message, such that the stream can be resumed and lost messages can be retransmitted.
  • Client message notifications as DOM events


  • Each SSE consumes 1 TCP connection in HTTP/1.1 due to the long-lived HTTP connection


Facebook/Twitter updates, stock price updates, news feeds, sport results, etc.

Difference Between Push Technology


  • Browser

    • EventSource interface allows the client to receive push notifications from the server as DOM events
    • Implement XHR streaming and handle all the connection management and message parsing (allowing our applications to focus on the business logic)
  • Server

    • “Event Stream” data format is used to deliver the individual updates
    => Request
    // Client connection initiated via EventSource interface
    GET /stream HTTP/1.1 
    Host: example.com
    Accept: text/event-stream
    <= Response
    // Server response with "text/event-stream" content-type
    HTTP/1.1 200 OK 
    Connection: keep-alive
    Content-Type: text/event-stream
    Transfer-Encoding: chunked
    // Server sets client reconnect interval 15s if the connection drops
    retry: 15000 
    // Simple text event with no message type
    data: First message is a simple string. 
    // JSON payload with no message type
    data: {"message": "JSON payload"} 
    // Simple text event of type "foo"
    event: foo 
    data: Message of type "foo"
    // Multiline event with message ID and type
    id: 42 
    event: bar
    data: Multi-line message of
    data: type "bar" and id "42"
    // Simple text event with optional ID
    id: 43 
    data: Last message, id "43"


We use PHP because “PHP is the best language for web programming.

Demo source code from: HTML5 Server-Sent Events(伺服器發送事件) 教學範例 for PHP


The first PCAP file is a normal version, the second one calls new EventSource() 3 times so there are 3 TCP connections in Wireshark conversations window.




ini_set('max_execution_time', 0);

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

// Infinite loop
while (true) {
    // Data for transmission
    $data = array(
        'name' => 'Mr. Wang',
        'date' => date('Y-m-d H:i:s')

    // Send with JSON encoded format
    echo "id: D121xxxxxx\ndata: " . json_encode($data);
    echo "\n\n";


    // wait for 1 second


<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>HTML5 API Server-Send Event</title>

<script type="text/javascript">
window.onload = function() {
    document.querySelector('button').addEventListener('click', closeSSE, false);

    if (typeof(EventSource) !== 'undefined') {
        // server path
        var sse = new EventSource('server.php');

        sse.addEventListener('open', open, false);
        sse.addEventListener('message', message, false);
        sse.addEventListener('error', error, false);
    } else {
        alert("Browser doesn't support");

    function closeSSE(event) {

    function open(event) {

    function message(event) {
        var pullData = JSON.parse(event.data);
        var newElement = document.createElement('li');
        newElement.innerHTML = pullData.name + ', ' + pullData.date;

    function error(event) {

        alert('connection error');

    function closeEventSource() {





comments powered by Disqus