49

since I'm using WebSocket connections on more regular bases, I was interested in how things work under the hood. So I digged into the endless spec documents for a while, but so far I couldn't really find anything about chunking the transmission stream itself.

The WebSocket protocol calls it data frames (which describes the pure data stream, so its also called non-control frames). As far as I understood the spec, there is no defined max-length and no defined MTU (maximum transfer unit) value, that in turn means a single WebSocket data-frame may contain, by spec(!), an infinite amount of data (please correct me if I'm wrong here, I'm still a student on this).

After reading that, I instantly setup my little Node WebSocket server. Since I have a strong Ajax history (also on streaming and Comet), my expectations originaly were like, "there must be some kind of interactive mode for reading data while it is transfered". But I am wrong there, ain't I ?

I started out small, with 4kb of data.

server

testSocket.emit( 'data', new Array( 4096 ).join( 'X' ) );

and like expected this arrives on the client as one data-chunk

client

wsInstance.onmessage = function( data ) {
    console.log( data.length ); // 4095
};

so I increased the payload and I actually was expecting again, that at some point, the client-side onmessage handler will fire repeatly, effectivley chunking the transmission. But to my shock, it never happened (node-server, tested on firefox, chrome and safari client-side). My biggest payload was 80 MB

testSocket.emit( 'data', new Array( 1024*1024*80 ).join( 'X' ) );

and it still arrived in one big data-chunk on the client. Of course, this takes a while even if you have a pretty good connection. Questions here are

  • is there any possiblity to chunk those streams, similar to the XHR readyState3 mode ?
  • is there any size limit for a single ws data-frame ?
  • are websockets not supposed to transfer such large payloads? (which would make me wonder again why there isn't a defined max-size)

I might still look from the wrong perspective on WebSockets, probably the need for sending large data-amounts is just not there and you should chunk/split any data logically yourself before sending ?

jAndy
  • 231,737
  • 57
  • 305
  • 359

2 Answers2

89

First, you need to differentiate between the WebSocket protocol and the WebSocket API within browsers.

The WebSocket protocol has a frame-size limit of 2^63 octets, but a WebSocket message can be composed of an unlimited number of frames.

The WebSocket API within browsers does not expose a frame-based or streaming API, but only a message-based API. The payload of an incoming message is always completely buffered up (within the browser's WebSocket implementation) before providing it to JavaScript.

APIs of other WebSocket implementations may provide frame- or streaming-based access to payload transferred via the WebSocket protocol. For example, AutobahnPython does. You can read more in the examples here https://github.com/tavendo/AutobahnPython/tree/master/examples/twisted/websocket/streaming.

Disclosure: I am original author of Autobahn and work for Tavendo.

More considerations:

As long as there is no frame/streaming API in browser JS WebSocket API, you can only receive/send complete WS messages.

A single (plain) WebSocket connection cannot interleave the payload of multiple messages. So i.e. if you use large messages, those are delivered in order, and you won't be able to send small messages in between while a big message is still on the fly.

There is an upcoming WebSocket extension (extensions are a builtin mechanism to extend the protocol): WebSocket multiplexing. This allows to have multiple (logical) WebSocket connections over a single underlying TCP connection, which has multiple advantages.

Note also: you can open multiple WS connections (over different underlying TCPs) to a single target server from a single JS / HTML page today.

Note also: you can do "chunking" yourself in application layer: send your stuff in smaller WS messages a reassemble yourself.

I agree, in an ideal world, you'd have message/frame/streaming API in browser plus WebSocket multiplexing. That would give all the power and convenience.

oberstet
  • 21,353
  • 10
  • 64
  • 97
  • 2
    so if my quick calc is correct, thats a frame size of about *83 TB*, which pretty much equals infinity in this instance. Yet, there is no (browser-) API which allows for intercepting transmission streams ? Sounds wierd. – jAndy Oct 22 '12 at 13:03
  • 2
    Yep. Keep in mind that most implementations that don't have streaming API either have an explicit (configurable) frame (or message) size limit, or will just die because of lack of memory. Also keep in mind that even if browsers would provide a frame-based API and a server would send large messages in small frames, intermediaries might reassemble frames. Hence you'd really want message, frame and streaming based APIs in JS. It's not there. You might ask the W3C to specify one;) – oberstet Oct 22 '12 at 13:21
  • 1
    I indeed might send some mails to WHATWG list, why not :-). Thanks for time & answer so far. – jAndy Oct 22 '12 at 13:33
  • 3
    thx for bounty;) should you get some interesting feedback from WHATWG, I'd be interested also (i.e. leave a comment here) .. – oberstet Oct 31 '12 at 12:28
  • @oberstet, You ~state "other Websocket clients exist besides the browser implementations", but seriously **who else** uses Websocket unless for browsers **?** If you are not going to use the browser as a client, there's no point for Websockets in the first place since raw TCP/IP is preferred. – Pacerier Mar 16 '15 at 07:02
  • @Pacerier Compared to raw TCP, using WebSocket provides compatibility with existing network infrastructure and you can use one backend for all clients - browsers and others. – oberstet Mar 16 '15 at 10:17
  • @oberstet, I mean when we are **not** communicating with browsers, [What benefit does Websocket give](http://lucumr.pocoo.org/2012/9/24/websockets-101/#lessons-learned)? – Pacerier Mar 19 '15 at 15:14
  • Just out of curiosity @oberstet, this is 3 years after our conversation here, has there anything changed in browsers WS implementation to your knowledge, concerning multiplexing and frame/streaming ? – jAndy Oct 08 '15 at 13:34
  • 2
    @jAndy support for WebSocket (RFC6455) is now virtually universal, support for WebSocket compression is following up (the RFC is done now). Rgd multiplexing or a browser API with frame/streaming support: nope. – oberstet Oct 08 '15 at 16:42
  • 1
    Looks like a [WebSocketStream](https://web.dev/websocketstream/) API is under development now. – Jeremy Field Apr 28 '21 at 21:16
10

RFC 6455 Section 1.1:

This is what the WebSocket Protocol provides: [...] an alternative to HTTP polling for two-way communication from a web page to a remote server.

As stated, WebSockets are for commmunications between a web page and a server. Please note the difference between a web page and a web browser. Examples being used are browser games and chat applications, who excange many small messages.

If you want to send many MB's in one message, I think you're not using WebSockets the way they were intended. If you want to transfer files, then do so using a Plain Old Http Request, answered with Content-Disposition to let the browser download a file.

So if you explain why you want to send such large amounts of data, perhaps someone can help come up with a more elegant solution than using WebSockets.

Besides, a client or server may refuse too large messages (although it isn't explicitly stated how it'll refuse):

RFC 6455 Section 10.4:

Implementations that have implementation- and/or platform-specific limitations regarding the frame size or total message size after reassembly from multiple frames MUST protect themselves against exceeding those limits. (For example, a malicious endpoint can try to exhaust its peer's memory or mount a denial-of-service attack by sending either a single big frame (e.g., of size 2**60) or by sending a long stream of small frames that are a part of a fragmented message.) Such an implementation SHOULD impose a limit on frame sizes and the total message size after reassembly from multiple frames.

Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • Your saying that, in order for one user to send a file to another user, the user should send the file to the server to be temporarily persisted somewhere. Then notify the recipients that they need to make a request to an http endpoint to retrieve the file. Then delete the persisted file once recipients retrieve it. As opposed to just sending the file, once received, straight away to the recipients without all the intermediary steps? – Spankied Nov 04 '21 at 03:06
  • It's how websockets work. If you want peer-to-peer, then use a protocol that supports hole punching to traverse NAT and so on. See https://stackoverflow.com/questions/1032006/will-html5-allow-web-apps-to-make-peer-to-peer-http-connections, WebRTC for example. – CodeCaster Nov 04 '21 at 07:05
  • Im developing a chess game where users send moves over WS, along with other game/room state. While in game room users can share music files. Your saying I should use a completely separate protocol to share music, because its not what WS was intended for? – Spankied Nov 04 '21 at 08:47
  • @Spankied if you want to create a peer-to-peer file sharing program, use a file sharing protocol. Not WebSockets. – CodeCaster Nov 04 '21 at 13:37
  • Ended up uploading file to s3 and notifying users in room song was shared. Then users can make an http request to retrieve the file. Had this all implemented over WS before building out a real backend using AWS, which limits payload size. Thanks for believing in me. – Spankied Nov 04 '21 at 14:13