Contents


HTTP server push with WebSocket and SSE

Real-time data streaming for your web and mobile applications

Comments

HTTP server push, also known as HTTP streaming, is a client-server communication pattern that sends information from an HTTP server to a client asynchronously, without a client request. A server push architecture is especially effective for highly interactive web or mobile applications, where one or more clients need to receive continuous information from the server. In this article you'll learn about WebSocket and SSE (server-sent events), two technologies that implement HTTP server push.

I start with an overview of the technical differences between the two solutions and how each one is implemented in a web architecture. Using an example application, I show you how setup an SSE implementation, followed by a similar implementation for WebSockets. Finally, l compare the two technologies and offer my conclusions about using them for different types of web or mobile applications.

Note that this article assumes familiarity with the language and concepts of HTTP server push. Both applications are written in Python using CherryPy.

The limits of request-response

Client-server communication on the web has historically been a request-response model, requiring the client (such as a web browser) to request resources from the server. The server responds to a client request by sending the requested resource. If the resource is unavailable, or if the client doesn't have permission to access it, the server will send an error message. In a request-response architecture, a server can never send an unsolicited message to a client.

As web applications evolved to be more powerful and interactive, the limits of the request-response model started to show. Client applications needing more frequent updates were required to send more frequent GET requests. This technique, known as polling, can overload the server during peak periods, causing performance issues. It's inefficient because the client sends many requests that return without an update. Additionally, clients can only poll at specified intervals, which can slow client responsiveness.

HTTP server push technologies emerged to resolve the performance issues and other limitations associated with frequent polling. Especially for interactive web apps such as games or screen-sharing services, it is more efficient for the web server to send updates to the client whenever new data is available.

Comparing WebSocket and SSE

WebSocket and SSE are both alternatives to the traditional request-response web architecture, but they are not exactly competing technologies. A WebSocket architecture consists of a socket that is opened between the client and the server for full-duplex (bidirectional) communication. Instead of sending a GET message and waiting for a server response, the client simply listens to the socket, receiving server updates and using the data to initiate or support various interactions. A client can also use the socket to communicate with the server, for instance by sending an ACK message when an update has been successfully received.

SSE is a simpler standard, developed as an extension of HTML5. While SSE enables asynchronous messages from the server to the client, the client cannot send messages to the server. SSE's half-duplex communication model is best suited to applications where the client simply needs to receive streaming updates from the server. One advantage of SSE over WebSocket is that it works over HTTP, without requiring additional components.

For a multipurpose web application requiring extensive communication between client and server, WebSocket is the obvious choice. SSE is more suitable for applications that want to stream asynchronous data to the client from the server, but do not require a response back.

Browser support

Browser support is an essential factor when comparing HTTP protocols. From the browsing data in Table 1, you can see that the WebSocket protocol is supported across all modern browsers, including mobile ones. SSE isn't supported by Microsoft IE or Edge.

Table 1. Table 1. Browser usage in January 2017
2017ChromeIE/EdgeFirefoxSafariOpera
January73.7%4.9%15.4%3.6%1.0%

For applications that must run in all possible browsers, WebSocket is currently the better choice.

Development effort

Effort is another factor to consider when comparing protocols, especially newer ones like WebSocket and SSE. "Effort" here refers to lines of code, or how much time you'll put into coding your app using the given protocol. This metric is especially important for projects that have a strict time constraint or development budget.

SSE takes considerably less effort to implement than WebSocket. It works with any app written in HTML5, and mainly consists of adding an HTTP header to messages sent from the server to the client. Given the correct header, the client will automatically recognize the message as a server-sent event. Unlike WebSocket, SSE does not require you to establish or maintain a socket connection between the server and client.

The WebSocket protocol requires that you configure a server-side socket that listens for client connections. The client automatically opens a socket to the server and waits for messages, which can be sent asynchronously. Each application may define its own message format, keep-alive (heartbeat) strategy, and so on.

Table 2 summarizes the strengths and weaknesses of the SSE and WebSocket protocols.

Table 2. Table 2. Comparing SSE and WebSocket
SSEWebSocket
Type of communicationHalf-duplex (unidirectional)Full-duplex (bidirectional)
Browser supportCurrently not available in Microsoft browsers.Available in all major browsers.
Development effortMinor: Just need to send an HTTP message with a specific header.Medium: Need to establish and maintain a TCP socket communication. A listener socket on the server side is also required.

Now let's see how each of these technologies works in a simple web application example.

Developing an SSE application

SSE is an HTML5 standard that uses nothing but HTTP to deliver asynchronous messages. Unlike WebSocket, SSE does not require you to create a server socket on the backend, nor to open connections on the frontend. This removes considerable complexity right away.

The SSE frontend

Listing 1 shows the UI code for a simple HTTP server push application with SSE:

Listing 1. SSE frontend
var source = new EventSource(“/user-log-stream”);
source.onmessage = function(event) {
    var message = event.data;
    // do stuff based on received message
};

EventSource is an interface that creates the HTTP connection to the server. The server sends messages to the client using event stream format, a simple stream of text data that is encoded using UTF-8. Once contact has been established, the HTTP connection is kept open for the given message stream, so that the server can send updates. In Listing 1, we've created an HTTP connection to receive events related to the /#tabs/user-log-stream URI.

The SSE backend

On the backend, we create a dispatcher for the URL /user-log-stream. The dispatcher will receive connection requests from the frontend and send back an asynchronous message to initiate communication. The SSE client cannot send messages to the server.

The code stub in Listing 2 illustrates the backend code.

Listing 2. SSE backend
import cherrypy

class UserLogStream
    messages = []

    @cherrypy.expose
    def stream(self):
        cherrypy.response.headers["Content-Type"] = "text/event-stream"
        while True:
            if len(messages) > 0:
                for msg in messages:
                    data = “data:” + msg + “\n\n”
                    yield data
                 messages = []

routes_dispatcher = cherrypy.dispatch.RoutesDispatcher()
routes_dispatcher.connect(‘user-log-stream’, ‘/’, controller = UserLogStream(), action=’stream’)

The stream method of the UserLogStream class is assigned to every EventSource that connects to the /user-log-stream URI. Any pending messages will be sent to the connected EventSource. Note that the message format isn't optional: the SSE protocol requires that messages start with data: and end with \n\n. While HTTP is the message format used in these examples, it is possible to send messages using JSON or another format.

This is a very basic example of an SSE implementation, but demonstrates the simplicity of the protocol.

Developing a WebSocket application

Like the SSE example, the WebSocket application is built on top of CherryPy, a Python web framework. Project WoK is the backend web server, and the python-websockify library plugin handles socket connections. The plugin is part of Kimchi. Figure 1 shows the architecture of the WebSocket example application. As you can see, it's considerably more complex than SSE.

Figure 1. Architecture of a WebSocket application

The components are as follows:

  • Project WoK: The backend Python logic that supplies messages to be broadcast by the push server.
  • Websockify proxy: Implemented by the python-websockify library, the proxy receives messages from the push server using Unix sockets and proxies them to connected WebSockets on the UI.
  • Push server: A regular Unix socket server that broadcasts backend messages to connected clients.
  • Frontend: The UI establishes a WebSocket connection to the Websockify proxy and listens for server messages. Depending on the message a specific action is taken, for example refreshing a list or showing a message to the user.

Websockify proxy

The Websockify proxy is implemented by the python-websockify library. It is initialized using the CherryPy web server engine, as shown in Listing 3.

Listing 3. The Websockify proxy
    params = {'listen_host': '127.0.0.1',
              'listen_port': config.get('server', 'websockets_port'),
              'ssl_only': False}

    # old websockify: do not use TokenFile
    if not tokenFile:
        params['target_cfg'] = WS_TOKENS_DIR

    # websockify 0.7 and higher: use TokenFile
    else:
        params['token_plugin'] = TokenFile(src=WS_TOKENS_DIR)

    def start_proxy():
        try:
            server = WebSocketProxy(RequestHandlerClass=CustomHandler,
                                    **params)
        except TypeError:
            server = CustomHandler(**params)

        server.start_server()

    proc = Process(target=start_proxy)
    proc.start()
    return proc

The Websockify proxy binds any registered tokens to its WebSocket URI, as shown here:

Listing 4. Binding a registered token
def add_proxy_token(name, port, is_unix_socket=False):
    with open(os.path.join(WS_TOKENS_DIR, name), 'w') as f:
        """
        From python documentation base64.urlsafe_b64encode(s)
        substitutes - instead of + and _ instead of / in the
        standard Base64 alphabet, BUT the result can still
        contain = which is not safe in a URL query component.
        So remove it when needed as base64 can work well without it.
        """
        name = base64.urlsafe_b64encode(name).rstrip('=')
        if is_unix_socket:
            f.write('%s: unix_socket:%s' % (name.encode('utf-8'), port))
        else:
            f.write('%s: localhost:%s' % (name.encode('utf-8'), port))

Below is the command to add a Websockify proxy entry called myUnixSocket. This entry will proxy WebSocket connections to the Unix socket /run/my_socket:

add_proxy_token(‘myUnixSocket’, ‘/run/my_socket’, True)

In the UI, we open a WebSocket to the Unix socket using the following URI:

wss://<server_address>:<port>/websockify?token=<b64encodedtoken>

In the URI, b64encodedtoken is the base64 value of the myUnixSocket string without the = character. For more about this configuration, see the WebSocket proxy module.

The push server

There are two requirements for the push server:

  1. It must be able to handle multiple connections at the same time.
  2. It must be able to broadcast the same message to all connected clients.

For the purpose of this example, the push server will act as a broadcast agent. While the capability is there, we expect to receive no messages from the client side.

Listing 5 shows the relevant Python code for an initial version of the push server. (See Project WoK on GitHub for the final version of the pushserver module.)

Listing 5. WebSocket push server
class PushServer(object):

    def __init__(self):
        self.set_socket_file()

        websocket.add_proxy_token(TOKEN_NAME, self.server_addr, True)

        self.connections = []

        self.server_running = True
        self.server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        self.server_socket.setsockopt(socket.SOL_SOCKET,
                                      socket.SO_REUSEADDR, 1)
        self.server_socket.bind(self.server_addr)
        self.server_socket.listen(10)
        wok_log.info('Push server created on address %s' % self.server_addr)

        self.connections.append(self.server_socket)
        cherrypy.engine.subscribe('stop', self.close_server, 1)

        server_loop = threading.Thread(target=self.listen)
        server_loop.setDaemon(True)
        server_loop.start()


    def listen(self):
        try:
            while self.server_running:
                read_ready, _, _ = select.select(self.connections,
                                                 [], [], 1)
                for sock in read_ready:
                    if not self.server_running:
                        break

                    if sock == self.server_socket:

                        new_socket, addr = self.server_socket.accept()
                        self.connections.append(new_socket)
                    else:
                        try:
                            data = sock.recv(4096)
                        except:
                            try:
                                self.connections.remove(sock)
                            except ValueError:
                                pass

        except Exception as e:
            raise RuntimeError('Exception occurred in listen() of pushserver '
                               'module: %s' % e.message)


    def send_notification(self, message):
        for sock in self.connections:
            if sock != self.server_socket:
                    sock.send(message)

The __init__() of the PushServer() class initializes the Unix socket to listen for connections. We also add a token in the WebSocket proxy. This token will be used in the URI of the WebSocket connection by the frontend.

There are numerous ways to implement a server push architecture. For the purpose of a broadcasting server push implementation, I believe it is most intuitive to use a single listener thread (select.select()) to control known connections. In this case, the listen() method runs in a daemon thread and is responsible for managing existing connections. The select.select method runs as a non-blocking function that returns when any socket from the self.connections array is ready to be read, or after a one-second timeout. When a new connection is created, it is added in the self.connections array. When a connection is closed, it is removed from the array.

The listen() method can remove a socket from the array in one of two ways: either by detecting that the socket was closed in the except block of the recv() call, or by receiving a CLOSE message. The send_notification() method is responsible for broadcasting messages to clients that have subscribed to receive them. We use the except block to close the socket connection when a client unsubscribes. We cannot use the listen() alone to close a socket connection between the client and server, because doing so can lead to a broken pipe error in the send_notification() method.

I'll explain more about that in the next section.

Troubleshooting the WebSocket frontend

Backend setup and configuration has been relatively easy, but coding the frontend will not be so simple. From deciding whether to open or close a browser connection to differences in how we handle certain APIs, the WebSocket frontend presents a few challenges. As we resolve those challenges, we'll be required to revisit certain elements of the backend code.

Our first decision is whether to maintain a single WebSocket connection for all of the UI or setup multiple WebSocket connections to be opened on demand. We might first consider multiple, on-demand WebSocket connections. The idea is that a connection should only be opened when the UI is expecting a specific message from the backend—waiting for a certain task to complete, for example. If no asynchronous message is expected, no connection is needed. This spares resources from both the UI and the backend, but it comes at the cost of having to manage the lifecycle of each opened WebSocket.

The single, persistent WebSocket connection is more suitable for UIs that expect to receive a high volume of asynchronous messages from the server. The persistent connection is used as a notification stream, allowing any relevant UI code to listen and act on the messages being received.

Each solution has its place, and we will experiment with each one below. What is important is to use the approach that best suits your application.

Multiple WebSocket connections

Opening and closing a WebSocket is a matter of calling a constructor to open and call the close() method. Differences between browser behaviors such as page refresh can complicate things, however. Every time you open a WebSocket, a connection is made to the WebSocket proxy, and then to the actual push server on the backend. The connection is kept open until it is closed. Consider the following:

Listing 6. Calling refreshInterface() on each received message
notificationsWebSocket = new WebSocket(url, ['base64']);
notificationsWebSocket.onmessage = function(event) {
    refreshInterface();
};

In Listing 6, onmessage is an event handler that is called every time a new message is received in the socket. The refreshInterface() function is called when a message arrives. If multiple notificationsWebSocket objects are left open, refreshInterface() will be called multiple times as well.

Opening more WebSocket connections than we need is an obvious waste of resources for both the front- and backend. It also risks unintended behaviors. To work around these issues, we can avoid opening unnecessary WebSocket connections and close existing connections when they are no longer needed. Closing a WebSocket can be done manually or by the browser. In the browsers I’ve used for testing, Chrome and Firefox, both will close open WebSocket connections when the browser window is refreshed or closed, or when the domain is changed.

When a WebSocket is closed, two things happen:

  1. An event handler called onclose is called to do any additional cleanup.
  2. The closing side (in this case, the UI WebSocket) warns the other end by starting a closing handshake. In the process, a close frame is sent with the value of 0x8. Upon receiving this frame, the backend is supposed to stop sending data and immediately close its side of the socket. This process happens regardless of what type of cleanup has been configured in the onclose handler.

Ideally, the onclose handler is called and the push server receives the close frame. Unfortunately, that wasn't what happened when I tested the implementation at this stage.

Websockify proxy and the close frame

As of this writing, the Websockify proxy plugin (v0.8.0-2) does not forward a close frame to the Unix socket and on to the push server. Thus the connection in the UI socket may be closed, but the push server is unaware of it, and keeps its side of the connection open.

When the push server attempts to broadcasts a new message to all connections, including the closed socket, it will receive a broken pipe error. If not handled properly, this can lead to the push server being terminated. Here is the socket issuing a send_notification message:

Listing 7. send_notification leading to a broken pipe error
    def send_notification(self, message):
        for sock in self.connections:
            if sock != self.server_socket:
                    sock.send(message)

One way of handling this situation would be to wrap the sock.send() call in a try/except, then deal with the broken pipe exception. An alternative would be to create a unique close handshake, using the onclose event handler to send a message to the backend server. The push server side would then close its own socket connection, leaving the send_notification method unchanged.

We'll try that solution next.

Using onclose to send a close message

The onclose event handler will be called just before a WebSocket connection is terminated. However the connection is still alive at this point. Our idea is to allow for a clean end-of-connection by sending a close message to the push server, so that it knows the socket will be closed on the frontend. Basically, we’re emulating the close frame's role in the closing handshake.

Listing 8 shows the WebSocket sending a CLOSE message to the push server just before closing the connection:

Listing 8. Sending a CLOSE message in the onclose event
notificationsWebSocket = new WebSocket(url, ['base64']);
(...)
notificationsWebSocket.onclose = function() {
    notificationsWebSocket.send(window.btoa(‘CLOSE’));
};

In the push server, we listen to all incoming messages from the UI to see if the CLOSE message has been received. If it has, we close the socket immediately:

Listing 9. Push server response to CLOSE
    def listen(self):
        try:
            while self.server_running:
                read_ready, _, _ = select.select(self.connections,
                                                 [], [], 1)
                for sock in read_ready:
                    if not self.server_running:
                        break

                    if sock == self.server_socket:

                        new_socket, addr = self.server_socket.accept()
                        self.connections.append(new_socket)
                    else:
                        try:
                            data = sock.recv(4096)
                        except:
                            try:
                                self.connections.remove(sock)
                            except ValueError:
                                pass

                            continue
                        if data and data == 'CLOSE':
                            try:
                                self.connections.remove(sock)
                            except ValueError:
                                pass
                            sock.close()

Calling onclose before the WebSocket is closed and the CLOSE message is sent should prevent broken pipe errors in the send_notification routine. Unfortunately, while onclose works well in Firefox, testing shows that we're still geting broken pipe errors in the send_notification method when we open the application in Chrome. This is due to an unresolved issue in Google Chrome.

The single, persistent WebSocket connection

Issues with onclose and the close frame push us toward the single, persistent WebSocket connection. A single connection that will be closed only when the browser leaves the page (either by closing the window, reloading, or going to another URL) requires only that the push server closes the connection when a broken pipe occurs. A centralized connection would be easier to maintain in Project WoK, and WoK plugins would relieve us from implementing a WebSocket strategy from scratch.

Listing 10 shows a modified send_notification message. Note that it handles the broken pipe error by removing the socket from the valid connection list and then closing it:

Listing 10. send_notification for a single, persistent connection
        def send_notification(self, message):
        for sock in self.connections:
            if sock != self.server_socket:
                try:
                    sock.send(message)
                except IOError as e:
                    if 'Broken pipe' in str(e):
                        sock.close()
                        try:
                            self.connections.remove(sock)
                        except ValueError:
                            pass

Configuring the heartbeat

In a multiple connections strategy, you open a connection, use it, and then close it immediately. A single, persistent connection should be kept open indefinitely, even if nothing is being sent. However, testing in Chrome and Firefox shows that the WebSocket connection times out after a period of being idle, and the connection is terminated.

To avoid a browser timeout, we implement a simple heartbeat message to be sent periodically to the push server. This message keeps the WebSocket connection alive—the push server doesn't need to respond to it. The default timeout seems to be 300 seconds, but we can't assume all browsers implement the same timeout. To be safe, we set the heartbeat message to a 30-second interval, as shown in Listing 11:

Listing 11. Timeout interval
    notificationsWebSocket = new WebSocket(url, ['base64']);
    var heartbeat = setInterval(function() {
        notificationsWebSocket.send(window.btoa('heartbeat'));
    }, 30000);

This simple timeout will keep the WebSocket connection alive, and will not flood the push server with heartbeat messages.

Adding listeners

With a single permanent connection established, we can implement a straightforward listener strategy to allow any other UI code inside our example application to listen to messages. Here's the design:

  1. A listener is bound to a specific message format. Our example application sends many notification messages. Message binding allows us to register a listener for specific ones only.
  2. Upon receiving a message from the push server, the main WebSocket channel verifies its content and calls all listeners bound to that message.
  3. The WebSocket channel initiates listener cleanup. Listeners must be deprecated when a URI changes. For example, a listener on the Activity Log tab in the URI /#tabs/user-log should be deprecated when the user browses to the Settings tab in the /#tabs/settings URI. Cleanup is bound to the hashchange event, which is triggered every time the URL (location.hash) changes. Since we don’t need to do this process more than once, we can set the $.one()’ jQuery call to execute it just once.

Here is the code that handles listeners in the example application:

Listing 12. Listener config for Project WoK
wok.notificationListeners = {};
wok.addNotificationListener = function(msg, func) {
    var listenerArray = wok.notificationListeners[msg];
    if (listenerArray == undefined) {
        listenerArray = [];
    }
    listenerArray.push(func);
    wok.notificationListeners[msg] = listenerArray;
    $(window).one("hashchange", function() {
        var listenerArray = wok.notificationListeners[msg];
        var del_index = listenerArray.indexOf(func);
        listenerArray.splice(del_index, 1);
        wok.notificationListeners[msg] = listenerArray;
    });
};

And this is how messages are sent to each relevant listener:

Listing 13. Listener notification
wok.startNotificationWebSocket = function () {
    wok.notificationsWebSocket = new WebSocket(url, ['base64']);
    wok.notificationsWebSocket.onmessage = function(event) {
        var message  = window.atob(event.data);
         if (message === "") {
                continue;
        }
            
        var listenerArray = wok.notificationListeners[message];
        if (listenerArray == undefined) {
            continue;
        }
        for (var i = 0; i < listenerArray.length; i++) {
            listenerArray[i](message);
        }
    }
 };

With that, we're very close to being done. But there is one more issue we need to resolve first.

Merged messages

While the single connection works, the current implementation makes an invalid assumption about how messages will be received on the frontend. What if the push server sends two messages in quick succession, message1 and then message2? The code expects that the onmessage event will be triggered twice with content to be read in the WebSocket, but that's not necessarily true. In fact, it's more likely that the message received will be message1message2—a merged message.

Merging messages is common TCP socket behavior: when two or more messages are sent in quick succession, the receiving socket will queue them and the onmessage event will be triggered only once for them all. If we haven't planned for it, this behavior will break our code. The solution is to define a messaging format that lets the application determine where one message ends and another begins.

One popular option is the TLV format, standing for Type-Length-Value. In this format the message contains three fields: type, length, and value, in that order. Type and length are of fixed size, and value has a variable size declared by the length field. The type field can be used to distinguish message types such as string, boolean, binary, or integer. It can also be use for application-specific purposes, such as warning message, information message, error message, and so on.

For our purposes, however, TLV is overkill. Our messages will always be strings, so there is no need for a type field. To solve the concatenation of the messages, we could add a fixed-length field declaring how many chars belong to the message. An even simpler approach would be to add an end-of-message marker to every message being sent. After receiving a message buffer, the frontend parses it using the message marker, splitting it into individual frontend messages.

Listing 14 shows the send_notification method modified with an end-of-message marker:

Listing 14. End-of-message marker in send_notification
END_OF_MESSAGE_MARKER = '//EOM//'

    def send_notification(self, message):
        message += END_OF_MESSAGE_MARKER
        for sock in self.connections:
            if sock != self.server_socket:
                try:
                    sock.send(message)
                except IOError as e:
                    if 'Broken pipe' in str(e):
                        sock.close()
                        try:
                            self.connections.remove(sock)
                        except ValueError:
                            pass

Note the //EOM// sequence in Listing 14. Any sequence can be used to denote the end of the message, as long as it isn't something that could be included in a valid message—like end.

Listing 15 shows the finished frontend code after adding the end-of-message parsing. See Project WoK for the complete source.

Listing 15. The WebSocket frontend
wok.notificationListeners = {};
wok.addNotificationListener = function(msg, func) {
    var listenerArray = wok.notificationListeners[msg];
    if (listenerArray == undefined) {
        listenerArray = [];
    }
    listenerArray.push(func);
    wok.notificationListeners[msg] = listenerArray;
    $(window).one("hashchange", function() {
        var listenerArray = wok.notificationListeners[msg];
        var del_index = listenerArray.indexOf(func);
        listenerArray.splice(del_index, 1);
        wok.notificationListeners[msg] = listenerArray;
    });
};

wok.notificationsWebSocket = undefined;
wok.startNotificationWebSocket = function () {
    var addr = window.location.hostname + ':' + window.location.port;
    var token = wok.urlSafeB64Encode('woknotifications').replace(/=*$/g, "");
    var url = 'wss://' + addr + '/websockify?token=' + token;
    wok.notificationsWebSocket = new WebSocket(url, ['base64']);

    wok.notificationsWebSocket.onmessage = function(event) {
        var buffer_rcv = window.atob(event.data);
        var messages = buffer_rcv.split("//EOM//");
        for (var i = 0; i < messages.length; i++) {
            if (messages[i] === "") {
                continue;
            }
            var listenerArray = wok.notificationListeners[messages[i]];
            if (listenerArray == undefined) {
                continue;
            }
            for (var j = 0; j < listenerArray.length; j++) {
                listenerArray[j](messages[i]);
            }
        }
    };

    var heartbeat = setInterval(function() {
        wok.notificationsWebSocket.send(window.btoa('heartbeat'));
    }, 30000);

};

Conclusion

Server-sent events is an elegant and simple solution for web applications that only need to be able to stream asynchronous server messages to the client. Being a half-duplex HTTP solution, SSE does not allow the client to stream messages back to the server. Moreover, at the time of this writing, SSE is not supported by any Microsoft browser. Whether this is a dealbreaker depends on your application's target audience.

WebSocket is more complex and demanding, but the full-duplex TCP connection makes it useful for a wider range of application scenarios. WebSocket is supported by most modern web frameworks, and is compatible with all major web and mobile browsers. While not demonstrated, it's possible to use a server framework like Tornado, which would allow you to quickly configure the push server rather than coding it from scratch.

SSE is a simpler and faster solution, but it isn't extensible. If your web application requirements were to change (for example, if you determined the frontend should interact with the backend) you would need to refactor your application using WebSocket. WebSocket presents more upfront work, but is a more versatile and extensible framework. It is better suited to complex applications that will add new features over time.


Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Information Management, Mobile development
ArticleID=1045439
ArticleTitle=HTTP server push with WebSocket and SSE
publish-date=05052017