websocketstream-explainer
websocketstream-explainer copied to clipboard
Making WebSocket.send() reliable
Hello,
WebSocketStream is a great proposal to solve backpressure and cases where sockets are overflown with too many messages. The extension mechanism will also bring needed improvements to WebSockets.
However WebSockets suffer from a huge caveat: currently there is no way to ensure that a message has been sent through the websocket. None. Over a bad network (e.g. hotel wifis), a SINGLE message sent can fail silently with zero means to detect it.
That's a huge problem for reliability, and WebSocketStream are a great opportunity to solve this issue (as nothing else is evolving - See https://github.com/whatwg/html/issues/4727).
This is vital for WebSockets and WebSocketStream: as more and more people find out that WebSockets are unreliable, all the work done around the WebSocketStream may turn useless.
Technical details:
Currently WebSocket.send()
stores the message in a buffer and returns immediately.
There is no guarantee that the message will ever be sent.
Even ws.bufferedAmount
is not reliable, as it may decrease when the connection is down (messages are passed to the underlying OS, and falsely considered to be sent).
Also, ws.send()
throws an error when the WebSocket is in readyState CONNECTING
, but fails silently when the WebSocket gets closed, which can easily occur with bad Wifi (high latency and dropped packets).
The only way to guarantee transmission is to implement an acknowledgement protocol on top of websockets, which is silly as it is already implemented at low level!
WebSocket.send()
should return a Promise (or have a callback mechanism) that resolves when the message has been successfully sent through the network, and rejects when the message won't be able to be sent (and provides the message that failed).
When searching the internet about this problem, the comments commonly found are "WebSockets are unreliable". It should not be so!
Thank you for helping the Internet become a better medium!
This is really interesting, thank you.
WebSocketStream as implemented in Chrome at the moment solves one and a half of these issues: write()
will fail if the WebSocket is already closed, and it returns a promise which doesn't resolve until the data has been forwarded to the browser process. It may be possible to delay that until the data has been dispatched to TCP or TLS, I am not sure.
There's a limit to how much guarantee we can provide. The OS doesn't tell us when a packet we sent has been acked, so we simply don't know whether the message was delivered to the remote machine. This implies to me that application-level acknowledgement is always going to be necessary for completely reliable delivery.
The best guarantee you can get at the moment is that if the closed
promise resolves successfully, then all buffered messages were sent and the closing handshake succeeded, implying they were delivered at least as far as the WebSocket implementation in the server.
It may be possible to use the WebSocket Ping frame to get similar guarantees even without closing the connection, but IIRC the server can disregard ordering when sending back a Pong frame.
Hi, I am facing same problem, I want to know the time when my messages (which I am sending through websocket) are getting delivered to endpoint. But could not figure out any proper solution.
This needs to be done by implementing ACKs in the application layer. Even if we extended the WebSocket protocol to have explicit ACKs, it still wouldn't be a guarantee that the app had correctly received and processed the message.
This needs to be done by implementing ACKs in the application layer. Even if we extended the WebSocket protocol to have explicit ACKs, it still wouldn't be a guarantee that the app had correctly received and processed the message.
Thanks for the prompt reply. I would certainly try for some alternatives then