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 ?
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.
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.
Related
Is it possible to implement a WebService over a WebRTC Data Channel ?
The idea is:
The client makes one https request to the server for signaling and session establishment
The client and the server start to communicate via a WebRTC DataChannel bidirectionally
Benefits?:
Performance ?
Requests goes over one connection and the standard allows for multiple datachannels over the same connection ( ports )
Flexible networking topologies
UDP
End to end encryption
The server can send events over the same connection
Load balancing could be implemented from a pool of servers client side without a load balancer , or all kinds of different solutions
Currently being debated the addition of DataChannels to Workers/Service Workers/ etc https://github.com/w3c/webrtc-extensions/issues/64
Drawbacks:
Application specific code for implementing request fragmentation and control over buffer limits
[EDIT 3] I don't know how much of a difference in terms of performance and cpu/memory usage will it be against HTTP/2 Stream
Ideas:
Clients could be read replicas of the data for sync, or any other applications that are suitable for orbit-db https://github.com/orbitdb/orbit-db in the public IPFS network, the benefit of using orbit-db is that only allows to the owner to make writes, then the server could additionally sign with his key all the data so that the clients could verify and trust it's from the server, that could offload the main server for reads, just an idea.
[EDIT]
I've found this repo: https://github.com/jsmouret/grpc-over-webrtc
amazing!
[EDIT2]
Changed Orbit-db idea and removed cluster IPFS after investigating a bit
[EDIT3]
After searching Fetch PROS for HTTP/2 i've found Fetch upload streaming with ReadableStreams, i don't know how much of a difference will it be to run GRPC (bidi) over a WebRTC DataChannel or a HTTP/2 Stream
https://www.chromestatus.com/feature/5274139738767360#:~:text=Fetch%20upload%20streaming%20lets%20web,things%20involved%20with%20network%20requests).
Very cool video explaining the feature: https://www.youtube.com/watch?v=G9PpImUEeUA
Lots of different points here, will try to address them all.
The idea is 100% feasible. Check out Pion WebRTC's data-channels example. All it takes a single request/response to establish a connection.
Performance
Data channels are a much better fit if you are doing latency sensitive work.
With data channels you can measure backpressure. You can tell how much data has been delivered, and how much has has been queued. If the queue is getting full you know you are sending too much data. Other APIs in the browser don't give you this. There are some future APIs (WebTransport) but they aren't available yet.
Data channels allow unordered/unreliable delivery. With TCP everything you send will be delivered and in order, this issue is known as head-of-line blocking. That means if you lose a packet all subsequent packets must be delayed. An example would be if you sent 0 1 2 3, if packet 1 hasn't arrived yet 2 and 3 can't be processed yet. Data channels can be configured to give you packets as soon as they arrive.
I can't give you specific numbers on the CPU/Memory costs of running DTLS+SCTP vs TLS+WebSocket server. It depends on hardware/network you have, what the workload is etc...
Multiplexing
You can serve multiple DataChannel streams over a single WebRTC Connection (PeerConnection). You can also serve multiple PeerConnections over a single port.
Network Transport
WebRTC can be run over UDP or TCP
Load Balancing
This is harder (but not intractable) moving DTLS and SCTP sessions between servers isn't easy with existing libraries. With pion/dtls it has the support to export/resume a session. I don't know support in other libraries however.
TLS/Websocket is much easier to load balance.
End to end encryption
WebRTC has mandatory encryption. This is nice over HTTP 1.1 which might accidentally fall back to non-TLS if configured incorrectly.
If you want to route a message through the server (and not have the server see it) I don't think what protocol you use matters.
Topologies
WebRTC can be run in many different topologies. You can do P2P or Client/Server, and lots of things in between. Depending on what you are building you could build a hybrid mesh. You could create a graph of connections, and deploy servers as needed. This flexibility lets you do some interesting things.
Hopefully addressed all your points! Happy to discuss further in the comments/will keep editing the question.
I was also wondering about this HTTP-over-WebRTC DataChannel idea a couple of years ago. The problem at hand was how to securely connect from a web app to an IoT device (raspberry pi) that sits behind a firewall.
Since there was no readily available solution, I ended up building a prototype. It did the job and has been in live deployment since 2019.
See this technical blog post that covers the design and implementation in more detail:
https://webrtchacks.com/private-home-surveillance-with-the-webrtc-datachannel/
High level architecture:
Simplified sequence diagram:
Recently began the process of extracting the code into a standalone repo.
https://github.com/ambianic/peerfetch
If your main use-case exchanges small content, you may have a look at CoAP RFC 7252. A peer may easily implement both roles, client and server, though the exchanged messages for request and response share the same fomat.
For some advanced usage of DTLS 1.2, DTLS Connection ID can do some magic for you.
If you don't stick to javascript and java is an option, you may check the open source project Eclipse/Californium. That's a CoAP/DTLS implementation, which comes with DTLS Connection ID and some prepared advanced examples as built-in-cid-load-balancer-support or DTLS-graceful-restart.
Some background: I am working with legacy code, and am attempting to upload a binary file (~2MB) to an embedded microhttpd web server via an HTTP form (POST request). Lately I've noticed that the upload speed from Windows 10 machines is significantly slower than from non-Windows 10 machines: the upload will send a handful of bytes at a time (about 6-7 chunks of ~1500 bytes each) and will then pause, sometimes for 30-60 seconds, before sending another handful of bytes. The decrease in speed caused by this issue renders the whole upload process unusable.
I performed some debugging on the embedded server and found that it was indeed waiting for more data to come in on the socket created for the POST request, and that for some reason this data was not being sent by the client machine. After some analysis in Wireshark on Win10 vs non-Win10 traffic, the messages I am seeing appear to tally up with the issue described by Microsoft here: https://support.microsoft.com/en-gb/help/823764/slow-performance-occurs-when-you-copy-data-to-a-tcp-server-by-using-a.
Specifically, I am seeing that in the case of Windows 10, the first TCP packet sent to the embedded web server is indeed "a single send call [that] fills the whole underlying socket send buffer", as per the Microsoft article. This does not appear to be the case for non-Windows 10 machines. Hence, I need to be able to set up my sockets so that the web client does not send so much data as to fill up the receive buffer in one packet.
Unfortunately, major modifications to the web server itself (aside from little config tweaks) are out of the question, since the legacy code I'm working with is notoriously coupled and unstable. Therefore, I'm looking for a way to specify socket settings via JavaScript, if this is possible. I'm currently using the JQuery Form plugin, which operates on top of XMLHttpRequests. Since I have complete control over both the JavaScript page and the embedded web backend, I can hard-code the socket buffer sizes appropriately in both cases.
Is there a way to tweak low-level socket settings like this from JavaScript? If not, would there be another workaround for this issue?
There is no way you can do the TCP stack specific tuning you need from inside Javascript running inside a browser on the client side. It simply does not allow this kind of access.
I am running SocketIO on NodeJS and I don't care much about wide browsers support as it's my pet project where I want to use all the power of new technologies to ease the development. My concern is about how I should send large amounts of JSON data from server to client and back. Well, these amounts are not as large as could be for video or image binary data, I suppose not larger than hundreds of kilobytes per request.
Two scenarios I see are:
Send a notification via WebSockets from server to client that some data should be fetched. Then client code runs a regular XHR request to server and gets some data via XHR.
Send the whole data set over WebSockets from server to client. In this case I don't need to run any additional requests - I just get all the data via WebSockets.
I saw first case in Meteor.js, so I wondered the reasons of it.
Please share your opinion.
Websockets should support large data sets (up to 16 exabyte in theory), so from that point of view it should work fine. The advantage of XHR is that you will be able to observe progress over time and in general better tested for large data blocks. For example, I have seen websocket server implementations which (thinking retrospectively) wouldn't handle large data well, because they would load the entire data into memory (rather than streaming the data), but that's of course not necessarily the case for socket.io (dunno). Point in case: try it out with socket.io whilst observing memory usage and stability. If it works, definitely go with websockets, because long term the support for big data packages will only get better and definitely not worse. If it turns out to be unstable or if socket.io can't stream larger data files, then use the XHR construct.
Btw, just a google search turned up siofile, haven't looked into it that much, but it might be just the thing you need.
I currently have a javascript library that is using a JSON file to print them on the screen in an interactive way. (::We are using D3JS Library)
When we are on a client, we can easily delete, edit and create some nodes, that are updated in the JSON every 5-10 seconds.
The problem comes from two main facts :
First the automatic function that call itself every x seconds could make data corruption if we are doing some stuff on the datas already represented on the screen.
Then the project has been made in order to permit 5 people to interact together. When they are present onto the same session we cannot decently make them refresh every 5 seconds, that cause many overhead and doesn't avoid data corruption.
We have mainly thought about a solution only made with javascript and some AJAX but we realize that it should be reconsidered with a trigger that inform the client that the datas are no longer OK.
We are thinking currently about opening a script onto a server in order to attribute on each client an ID.
The goal would be to detect the modification done on the JSON file (on the server). But the point where we are stuck is :
1) Is there a best scripting language to interact server/web?
2) Which type of things should we use to make the clients update their datas? (socket right?)
About the second point the easiest way would be to call a JS function be we aren't aware of the possibilities given by the shell codes...
Sorry about the fact that we are happy developpers but maybe not enough skilled to solve this problem.
Thanks for your helps !
You can achieve that using pure javascript with the new WebSocket feature.
http://www.html5rocks.com/en/tutorials/websockets/basics/
Edit:
WebSocket is a web technology providing full-duplex communications channels over a single TCP connection. The WebSocket API is being standardized by the W3C, and the WebSocket protocol has been standardized by the IETF as RFC 6455.
WebSocket is designed to be implemented in web browsers and web servers, but it can be used by any client or server application. The WebSocket Protocol is an independent TCP-based protocol. Its only relationship to HTTP is that its handshake is interpreted by HTTP servers as an Upgrade request.[1] The WebSocket protocol makes possible more interaction between a browser and a web site, facilitating live content and the creation of real-time games. This is made possible by providing a standardized way for the server to send content to the browser without being solicited by the client, and allowing for messages to be passed back and forth while keeping the connection open.
Is there any advantages of having two distinct websocket connections to the same server from the same client? To me this seems a bad design choice, but is there any reason why/where it should work out better?
There are several reasons why you might want to do that but they probably aren't too common (at least not yet):
You have both encrypted and unencrypted data that you are sending/receiving (e.g. some of the data is bulky but not sensitive).
You have both streaming data and latency sensitive data: imagine an interactive game that occasionally has streamed video inside the game. You don't want large media streams to delay receipt of latency sensitive normal game messages.
You have both textual (e.g. JSON control messages) and binary data (typed arrays or blobs) and don't want to bother with adding your own protocol layer to distinguish since WebSockets already does this for you.
You have multiple WebSocket sub-protocols (the optional setting after the URI) that you support and the page wants to access more than one (each WebSocket connection is limited to a single sub-protocol).
You have several different WebSocket services sitting behind the same web server and port. The way the client chooses per connection might depend on URI path, URI scheme (ws or wss), sub-protocol, or perhaps even the first message from client to server.
I'm sure there are other reasons but that's all I can think of off the top of my head.
I found that it can make client logic much simpler when you are only subscribing to updates of certain objects being managed by the server. Instead of devising a custom subscription protocol for a single channel, you can just open a socket for each element.
Let's say you obtained a collection of elements via a REST API at
http://myserver/api/some-elements
You could subscribe to updates of a single element using a socket url like this:
ws://myserver/api/some-elements/42/updates
Of course one can argue that this doesn't scale for complex pages. However, for small and simple appications it might make your life a lot easier.