top | item 39745993

WebSockets vs. Server-Sent-Events vs. Long-Polling vs. WebRTC vs. WebTransport

518 points| bubblehack3r | 2 years ago |rxdb.info

228 comments

order
[+] kellengreen|2 years ago|reply
I've always had a bit of a soft spot for Server Sent Events. Just simple and easy to use/implement.
[+] shams93|2 years ago|reply
With ipv6 they can now be fully scaled easily but they are absolutely awesome, much easier to scale because you can give your client a simple list of sse services and its essentially stateless if done right.

Websockets get really complex to scale past a certain level of use.

[+] ajvpot|2 years ago|reply
I agree. Unfortunately you can only have 6 SSE streams per origin per browser instance, so you may be limited to 6 tabs without adding extra complexity on the client side.

https://crbug.com/275955

[+] paulddraper|2 years ago|reply
The downside is that you have to base64 payloads or otherwise remove newlines.

I wonder why they didn't just a multipart streamed response.

Supports my metadata, very commonly implemented format

[+] djbusby|2 years ago|reply
Works with bog-standard Apache prefork and PHP.
[+] fswd|2 years ago|reply
don't forget the timeout reconnect!
[+] marban|2 years ago|reply
Absolutely underrated.
[+] spintin|2 years ago|reply
SSE are really a subset of Comet-Stream (eternal HTTP response with Transfer-Encoding: chunked) only they use a header (Accept: text/event-stream) and wraps the chunks with "data:" and "\n\n".

But yes it's the superior (simplest, most robust, most performant and scalable) way to do real-time for eternity.

The browser is dead, but SSE will keep on doing work for native apps.

[+] apitman|2 years ago|reply
A few additional cons to be aware of:

WebSockets lack flow control (backpressure) and multiplexing, so if you need them you either roll your own or use something similar to RSocket.

Also SSE can't send binary data directly. You have to base64 encode it or similar.

WebTransport addresses these and also solves head of line blocking. But I'm concerned that we might run into a similar problem as we had with going from Python2 to Python3 and IPv6. Too easy for people to keep using the old version, and too little (perceived) benefit to upgrading.

As long as browsers still work with TCP, some networks will continue to block UDP (and thus HTTP3/WebTransport) outright.

[+] pthatcherg|2 years ago|reply
The article's information about WebRTC is not accurate. You can do client/server WebRTC without a "signaling server". Just make the server do the signaling. It takes a few extra round trips, but it doesn't need to be an extra server. And WebRTC data channels work quite well as a replacement for WebSockets or SSE, especially if you want to avoid head-of-line blocking. And there are many libraries that will do pretty much all of the work for you, like Pion or str0m.

I also think calling the WebTransport API complex is overblown. If you don't want the more advanced things, you can ignore them. If you want to use it like a WebSocket, just open one bidirectional stream and you're basically done. If you want to avoid head-of-line blocking, just open a stream for every message. It's a little more complex, but it's not the kind of thing you need a library for. Github Copilot will probably write the code for you. It's true there aren't as many server libraries out there yet, since WebTransport is still maturing. And we're waiting for Safari to add support.

[+] cyanydeez|2 years ago|reply
> You can do client/server WebRTC without a "signaling server"

Huh. The signalling server is implemented in websocket, typically.

It cant be implemented in webrtc unless you propose a existing decentralization of existing clients to boostrap'

[+] rvanmil|2 years ago|reply
Or, if you’re building for clients with a traditional “enterprise” and “secure” IT infrastructure: add refresh buttons and call it a day. If there’s one thing in my experience that consistently fails in these environments and cannot be fixed due to endless red tape, it’s trying to make real-time work for these type of clients.
[+] palmfacehn|2 years ago|reply
Jetty/CometD will fall back to long polling if other transports are not available.
[+] cbsmith|2 years ago|reply
Honestly, all the techniques for this stuff have their problems, including the refresh button.
[+] chgs|2 years ago|reply
My browser has a refresh button. Alas your application likely breaks when I use it.
[+] eightnoteight|2 years ago|reply
websockets and sse are a big headache to manage at scale, especially backend, requires special observability, if not implemented really carefully on mobile devices its a nightmare to debug on frontend side

devices switch off network or slow down etc,... for battery conservation, or when you don't explicitly do the I/O using a dedicated API for it.

new connection setup is a costly operation, the server has to store the state somewhere and when this stateful layer faces any issue, clients keep retrying and timing out. forever stuck on performing this costly operation. it's not like there is an easy way to control the throughput and slowly put the load on database

reliability wise long polling is the best one IME, if event based flow is really important, even then its better to have a 2 layer backend, where frontend does long polling on the 1st layer which then subscribes to websockets to the 2nd layer backend. much better control in terms of reliability

[+] debarshri|2 years ago|reply
I cannot agree more with you. I have seen people shot themselves on foot with Websockets and SSE. Long Polling even though is expensive, is it most explainable and scalable approach in my opinion.
[+] pornel|2 years ago|reply
SSE supports long polling. You can make the server close the connection whenever you want. SSE supports automatic reconnection, and will even include the last ID seen to let the server continue seamlessly.
[+] nchmy|2 years ago|reply
A lot of this was addressed in the linked article - rxdb has mechanisms to mitigate many of your concerns...
[+] tdudhhu|2 years ago|reply
Not in the article by also relevant is short polling. While this does not send messages from a server to a client it can still be useful when all other options are not available (on shared hosting for example).

In my experience it even works great when the poll interval is long (for example 20 seconds) but when you also include the message list in each response. That way the client will be up to date when it interacts with the server: user presses a button -> the client sends a request to the server -> the server reponds with data and also a list of the latest messages.

[+] Fabricio20|2 years ago|reply
To this day I still dont know why WebSockets and SSE dont support sending headers on the initial request, such as Authorization. Leaving authentication on realtime services entirely up to whoever is implementing the service.

I may be wrong here and the spec suggests a good way to do it, but i've seen so many different approaches that at this point might as well say there's none.

[+] rexxars|2 years ago|reply
The EventSource API (the browser "client API" for Server-Sent Events) leaves a lot to be desired. While I am a maintainer of the most used EventSource polyfill[1], I've recently started a new project that aims to be a modern take on what an EventSource client could be: https://github.com/rexxars/eventsource-client.

Beyond handling the custom headers aspect, it also supports any request method (POST, PATCH..), allows you to include a request body, allows subscribing to any named event (the EventSource `onmessage` vs `on('named event')` is very confusing), as well as setting an initial last event ID (which can be helpful when restoring state after a reload or similar). And you can use it as an async iterator.

I love the simplicity of Server-Sent Events, but the `EventSource` API seem to me like a rushed implementation that just kinda stuck around.

[1]: https://github.com/eventsource/eventsource

[+] michaelt|2 years ago|reply
> To this day I still dont know why WebSockets and SSE dont support sending headers on the initial request

Doesn't the initial request get to send a full set of standard HTTP headers, cookies and all?

[+] dist-epoch|2 years ago|reply
They do send cookies.
[+] cbsmith|2 years ago|reply
There's always TLS certificates... ;-)
[+] omgtehlion|2 years ago|reply
Wait, what?? Been using these for years. Am I missing something?
[+] paol|2 years ago|reply
Oooh boy you touched a pet peeve. I mean who needs authentication on the modern Web right? /s

The even more irritating thing is that there is nothing preventing this, and every server I've tried supports it. It's only the browser WebSocket API that was designed without this. Cookies are the only thing browsers will deign to send in the initial request.

[+] skybrian|2 years ago|reply
This is probably naive, but it seems like assuming HTTP/2 or better, an EventSource combined with fetch() for sending messages should be just as good as any other protocol that uses a single TCP connection? And HTTP/3 uses UDP, so even better.

(This all assumes you only care about maintaining a connection when the tab is in the foreground.)

I’m wondering what problems people have run into when they tried this.

[+] cbsmith|2 years ago|reply
I always find articles like this amusing, because I designed an online auction system back in the late 90's. No XHR requests at all. Real-time updates were all handled with server-push/HTTP streaming. It wasn't easy to handle all the open connections at the time, but it could be done to an acceptable scale with the right architecture.
[+] kevmo314|2 years ago|reply
I kind of miss long polling. It was so stupidly simple compared to newer tech, and that's coming from someone who thinks WebRTC is the best thing since sliced bread.
[+] mmis1000|2 years ago|reply
SSE isn't really more complex than long polling. The only difference is the server don't close the connection immediately after sent the response. Instead, it wait for data again and send more response using the same stream.
[+] Animats|2 years ago|reply
Oh, if only it were that simple.

The networking that makes Second Life go uses long polling HTTPS for an "event channel", over which the server can send event messages to the clients. Most messages go over UDP, but a few that need encryption or are large go over the HTTPS/TCP event channel.

At the client end, C++ clients use "libcurl". Its default timeout settings are not compatible with long polling. Libcurl will break connections and make another request. This can result in lost or duplicated messages.

At the server end, Apache front-ends the actual simulation servers, to filter out irrelevant connection attempts (Random HTTP attacks that try any open port, probably). Apache has its own timeouts, and will abort connections, forcing the client to retry.

There's a message serial number to try to prevent this mechanism from losing messages. The Second Life servers ignore the serial number the client sends back as a check. Some supposedly compatible servers from Open Simulator skip sequential numbers.

The end result is an HTTPS based system which can both lose and duplicate what were supposed to be reliable messages. Some of those messages, if lost, will stall out the user's activity in the game. The people who designed this are long gone. The current staff was unaware of how bad the mess is. Outside users had to find the problem and document it. The company staff has been trying to fix this for months. It seems to be difficult enough to fix that the current action is to defer work on the problem.

So, no, long polling is not "stupidly simple".

The right way to do this is probably to send a keep-alive message frequently enough that the TCP and HTTPS levels never time out. This keeps Apache and libcurl on their "happy paths", which work.

[+] ramesh31|2 years ago|reply
>I kind of miss long polling. It was so stupidly simple compared to newer tech, and that's coming from someone who thinks WebRTC is the best thing since sliced bread.

I still use it all the time. There are plenty of applications where the request overhead is reasonable in exchange for keeping everything within the context of an existing HTTP API.

[+] niutech|2 years ago|reply
You can still use long polling with HTTP/2 nowadays, it isn't going nowhere.
[+] mirekrusin|2 years ago|reply
Jsonrpc over websockets is underrated tech. Simple, easy to implement, maps to programming language constructs (async functions, events, errors) which means code looks natural as any library/package/module usage devs are used to, can be easily decorated with type safety, easy to debug, log, optimize etc, works on current browsers and can be used for internal service to service comms, it's fast (ie. nodejs is using simdjson [0]), can be compressed if needed (rarely the need), we built several things on top of it ie. async generators (avoids head of line blocking, maps naturally to typescript/js async generators).

[0] https://github.com/simdjson/simdjson

[+] cbsmith|2 years ago|reply
It's been a while since I've used websockets, but at least the last time I did, "simple" wouldn't be the word I'd have used. All kinds of annoying issues between different browsers. SSE was generally much simpler.
[+] tschellenbach|2 years ago|reply
(I work at Stream, we power activity feeds, chat and video calling/streaming for some very large apps)

You should in most cases just use websockets with a keep-alive ping every 30 seconds or so. It's not common anymore to block websockets on firewalls, so fallback solutions like Faye/Socket.io are typically not needed anymore.

WebTransport can have lower latency. If you're sending voice data (outside of regular webrtc), or have a realtime game its something to consider.

[+] ambigious7777|2 years ago|reply
I'm making a WASM browser dungeon crawler game using WebTransport. It currently does not have great support -- namely Safari -- but because of other API incompatibilities I'm not planning on supporting Safari :P

WebTransport is a bit more work than other ones, like SSE, but the flexibility and performance make it work it IMO.

[+] londons_explore|2 years ago|reply
A decent number of corporate firewalls still don't support web sockets...

That means if you build something that requires web sockets, prepare to have a deluge of support/refund requests from the most valuable clients who think your site is broken.

I suggest just having a once-per-second polling fallback, perhaps with an info bar saying 'the network you are connected to is degrading your experience'.

[+] FZambia|2 years ago|reply
Hello, I am author of https://github.com/centrifugal/centrifugo. Our users can choose from WebSocket, EventSource, WebTransport (experimental for now, but will definitely stabilize in the future). WebRTC is out of scope as the main purpose is central server based real-time json/binary messaging, and WebRTC makes things much more complex since it shines for peer-to-peer and rich media communications.

What I'd like to add is that Centrifugo also supports HTTP-streaming – not mentioned by the OP – but this is a transport which has advantages over Eventsource - like possibility to send POST body on initial request from web browser (with SSE you can not), it supports binary, and with Readable Streams browser API it's widely supported by modern browsers.

Another thing I'd like to mention about Centrifugo - it supports bidirectional WebSocket fallbacks with EventSource and HTTP-streaming, and does this without sticky sessions requirement in distributed scenario. I guess nobody else have this at this point. See https://centrifugal.dev/blog/2022/07/19/centrifugo-v4-releas.... Which solves one more practical concern. Sticky sessions is an optimization in Centrifugo case, not a requirement.

If you are interested in topic, we also have a post about WebSocket scalability - https://centrifugal.dev/blog/2020/11/12/scaling-websocket - it covers some design decisions made in Centrifugo.

[+] lxe|2 years ago|reply
I love how SSE is just a "don't close the connection and just keep flushing data". I bet IE6 supports this.
[+] ascii78|2 years ago|reply
I've been reading about WebRTC, does anyone actually know if browser to browser communication actually works reliably in practice ? Specifically NAT traversal, been hesitant to research it further because of this issue, seems that most of the connection parts seem to be legacy voip related protocols.
[+] btown|2 years ago|reply
Is there a modern open-source solution for bridging a traditional stateless web application to real-time notifications - one that's implemented all the best practices from the OP? Something like pusher.com but on self-hosted infrastructure/k8s, where messages from clients are turned into webhooks to an arbitrary server, and the server can HTTP POST to a public/private channel that clients can subscribe to if they know the channel secret.

I've come across https://github.com/soketi/soketi and https://centrifugal.dev/ but not sure if there are more battle-tested solutions.

[+] bterlson|2 years ago|reply
Great comparison. Would love to see http response streaming added to the mix. I think a lot of use cases involving finite streams sent from server to client can be handled by the server streaming JSONL in the response. I tend to prefer this over SSE for finite data streams.
[+] mmis1000|2 years ago|reply
Server send event IS http response streaming. It is a standardized way to implement response streaming.
[+] dustedcodes|2 years ago|reply
HTTP (POST) AJAX calls + SSE is one of the simplest ways to implement bi-directional real time functionality. IMHO this is a much more robust and nicer way than web sockets for a huge amount of applications which use web sockets today.
[+] atum47|2 years ago|reply
I did some testing with using SSE to send push notifications to my phone if someone set off a sensor, worked kinda good, but the browser had to be running in the background in order for it to work. After that i implemented a chat for a meme app that I've created to share memes with my friends, using websocket (Open Swoole) it is working nicely also. Never tested to see how many clients it can handle at once, but i guess the bottleneck would be in my server, not the software.

Open Swoole is very easy to setup and there's lots of tutorials online. Got my ass kicked a little bit trying to making my websocket secure (wss) but I'm the end it worked fine.