Tuesday, February 11, 2020

Understanding Server-Sent Events


Photo by Víctor Vázquez on Unsplash


Since last few weeks, I have been checking with a lot of developers that I know, if they are aware of server-sent events. Surprisingly, very few of them know what is server-sent event. Let us understand this term.

What is SSE?

As the name suggests, it is an event that is sent or initiated by a server, and this is exactly what it does.
According to the wiki pageServer-Sent Events (SSE) is a server push technology enabling a client to receive automatic updates from a server via HTTP connectionThis is standardized as the part of HTML5. The server-sent event is an API that is operated through the EventSource interface
A client subscribes to a “stream” from a server and the server will send messages (“event-stream”) to the client until the server closes the stream. It is the server that decides when and what to send the client and when to end the stream.

Why should you know about SSE?

Since applications and their users need to be updated in real-time, you need Server-Sent Events. Displaying the latest data updates to your users may change their actions.

Why SSE? I have Websockets....

WebSockets indeed was a long awaited evolution in client/server web technology. It allows a single TCP socket connection to be established between the client and server which allows for bi-directional, full duplex, messages to be instantly distributed. Now there is a catch, if you read this meticulously, you would definitely notice that WebSockets are bi-directional. So if I need only unidirectional, WebSockets will cause latency issues.
SSE is uni-directional (server to client) which improves latency issues. If we are looking from a server load point of view, SSE is way much better option than WebSockets.

Fields

As per MDN Docs, an event stream is a simple stream of text data which must be encoded using UTF-8. Messages in the event stream are separated by a pair of newline characters. A colon as the first character of a line is in essence a comment, and is ignored.
Each message received has some combination of the following fields, one per line:

event

A string identifying the type of event described. If this is specified, an event will be dispatched to the listener for the specified event name; the website source code should use addEventListener() to listen for named events. The onmessage handler is called if no event name is specified for a message.

data

The data field for the message. When the EventSource receives multiple consecutive lines that begin with data:, it will concatenate them, inserting a newline character between each one. Trailing newlines are removed.

id

The event ID to set the EventSource object's last event ID value.

retry

The reconnection time to use when attempting to send the event. This must be an integer, specifying the reconnection time in milliseconds. If a non-integer value is specified, the field is ignored.

Examples

To use the server-sent events, we need to call an API from a client and then the server will keep on sending the events, till the stream is closed.

.Net Core

Create a controller with any route. Add the following code to the controller.
        [HttpGet]
        public async Task Get()
        {
            var response = Response;
            response.Headers.Add("Content-Type", "text/event-stream");

            for (var i = 0; true; ++i)
            {
                await response.WriteAsync($"data: Controller {i} at {DateTime.Now}\r\r");

                response.Body.Flush();
                await Task.Delay(5 * 1000);
            }
        }
In this implementation we are using the writeAsync method in from the Microsoft.AspNetCore.Http namespace. The writeAsync method will write the given text to the response body with the UTF-8 encoding.

HTML

   <html>

   <body>
    <script type="text/javascript">
        if (!!window.EventSource) {
            var source = new EventSource('https://localhost:44317/api/sse');
            source.addEventListener('message', function (e) {
                console.log(e.data);
            }, false)

            source.addEventListener('open', function (e) {
                console.log(e);
            }, false)

            source.addEventListener('error', function (e) {
                if (e.eventPhase == EventSource.CLOSED)
                    source.close()
                if (e.target.readyState == EventSource.CLOSED) {
                    console.log("Disconnected");
                }
                else if (e.target.readyState == EventSource.CONNECTING) {
                    console.log("Connecting...");
                }
            }, false)
        } else {
            console.log("Your browser doesn't support SSE")
        }
      </script>
    </body>

   </html>
When I ran the HTML, this is how the output was showing up in the console.

No comments:

Post a Comment