Concerning JavaScript & PHP WebSocket TCP packet clumping, with example below.
For some reason, when sending packets quickly on my VPS, or accessing my localhost through a domain pointing at my IP address, multiple packets will clump together. I am trying to stream, for this example, 20 (#100byte) packets per second. On the servers end, they ARE indeed being sent out at a steady rate, exactly every 50ms, making 20 per second.
However, when they get to the client, the client only processes new messages about every 1/4th of a second. Causing new packets only to be received at a rate of 4 per second or so...
What is causing this clumping of packets together? This problem does not occur when everything through localhost. What's weirder is that it streams smoothly on iPhone's iOS Mobile Safari, with no problem at all. BUT, it doesn't work at all on PC Safari, (because I haven't set this up to work correctly with the old Hixie-76 WebSocket format, I'm assuming Mobile Safari is already using the newer RFC 6455 or newer JavaScript compiler) I have tried multiple hosting companies, with the exact same results each time.
See the example below, hosted on InMotion's VPS:
http://www.hovel.me/script/serverControl.php
(Click [Connect] on the left, then [View Game] on the right).
The current packet received will jump about 5 every time, as each 5 packets are received at once, every 1/4th of a second. However, I've seen examples that can send a constant, quick stream of packets.
What causes this clumping together / packets to wait for each other?
EDIT: This HAS to be something to do with Nagle's algorithm, which collects & sends small packets together? I'll work towards trying to bypass this in PHP.
Even with this TCP_NODELAY set in PHP, the problem still stands. Why it works on an iPhone but not a PC is still throwing me off...
EDIT: Setting TCPNoDelay and TcpAckFrequency to 1 in the registry fixes this, but I can't expect every user to do that. There must be a client-side, bread & butter JavaScript way.
How can I have functionality replicating node.js' "socket.setNoDelay(true)", without using node.js?
In the end, the client not recognizing the Nagle's algorithm being disabled, along with it's acknowledge frequency still being set at around 200ms, was causing the intermediate network to hold the following packets in a buffer. Manually sending out an acknowledgement message to the server, every single time the client receives a message, will cause the network to immediately "wake-up" and continue to process the next packets, as opposed to holding them in a buffer.
For Example:
conn = new WebSocket(url);
conn.onmessage = function(evt){
Server.send('ACKNOWLEDGE BYTES'); // Send ACK to server immediately
dispatch('message', evt.data); //Do regular event procedures
};
This temporary solution works, however this will nearly double bandwidth usage, among other network problems. Until I can get the WebSocket on the clients end to correctly not 'standby' for server ack, and the network to push through messages immediately, this gets the packets through quicker without the buffer corking problem.
That's TCP. It is helping you by economizing on IP packets. Part of it is due to the Nagle algorithm but part of it may also be caused by the intermediate network.
Related
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 use setInterval to get my notification counter every 5 second, I thinks it's bad idea to getting those results. because if you stay on my site for a while you got a billion times of loading XHR loading.And if you use the facebook, you don't get lot of XHR.Here is my web site capture XHR:
My Code in file: notification.php:
function getnotificount(){
$.post('getnotificount.php', function(data) {
$('#notifi_count').html(data);
});
}
setInterval(function(){
getnotificount();
}, 5000);
Your code is ok. It is not 'loading more than 1 billion XHR request', it's starting (and finishing - as we can see) a request every X seconds and there's nothing wrong with that.
However it's not the best way to implement a push notification system. That would be websockets, which is a way for your client to 'listen' to messages from your server. There are frameworks for this, the most popular one (and the one that I recommend) being socket.io.
Your third and most advanced/modern solution would be implementing a service-worker-based notification system but I'm pretty sure that's way too complex and not suitable for you since you can't even understand your problem enough to describe it.
What you are doing is polling, which is making a new request to the server on a regular bases. For a http request, a TCP connection is opened, request / response are exchanged and the TCP connection closed. This is what happens every 5s in your case.
If you want a more light weight solution, have a look at websockets such as socket.io. Only one TCP connection is opened and maintained between the front and the back. This bidirectional conection enables the back to notify the front when something happens.
It is not a bad idea at all. It is called polling and it is used at many places as a means to get regularly data from the server. However, it is not the best, nor the most modern solution to do this. In fact, if your server supports WebSockets, then you should use them. You do not need to use NodeJS to use WebSockets, since WebSocket is a protocol of creating a duplex channel of communication between your server and the client. You should read about WebSockets. Also, you can use push notification, which is inferior to WebSockets in my opinion. And a hacky way is to use a forever frame.
According to SO accepted answer, the ping timeout must be greater than the ping interval, but according to the examples in official socket.io docs, the timeout is less than the interval. Which one is correct?? Also, what could be the ideal values for both the settings for a shared whiteboard application where the moderator should not be able to edit the canvas when disconnected (internet drops off).
According to the socket.io documentation:
Among those options:
pingTimeout (Number): how many ms without a pong packet to consider the connection closed (60000)
pingInterval (Number): how many ms before sending a new ping packet (25000).
Those two parameters will impact the delay before a client knows the server is not available anymore. For example, if the underlying TCP connection is not closed properly due to a network issue, a client may have to wait up to pingTimeout + pingInterval ms before getting a disconnect event.
This leads me to believe there is no dependency on one value being greater than another. You will likely want to set a higher timeout time to allow for slow network connection to receive a response. The interval time will be the time from a failure to attempt trying again and should be set long enough to allow reconnection, but not so long you are holding the connection.
As for ideal values, this will be application specific. A few things I would consider:
How responsive must your application be?
How long will your application take to respond to a network request?
How large is the data being passed back and forth?
How many concurrent users will you have?
These are just to name a few, for a small local application you would likely be fine with a timeout of 10000 and an interval of 5000, but this is an absolute guess. You will need to consider the previously mentioned bullet points.
3 mini questions regarding websocket connections
When the client sends data to the server there is latency. When the server sends data to the client is there latency or it is instant?
If the client sends data to the server VERY FAST in a specific row - let's say [1, 2, 3], is there any chance that, due to latency or other reasons, the data to be received by the server in a different row? ( like [2, 1, 3] )
(Same as question #2, but when the server sends the data)
Yes, there is latency. Its still a connection and there is still a chain to navigate. Latency only matters when things are changing and given that it takes X amount of time for the message to reach the client and another X ms for the client to do anything about it, its quite possible the state will change during those ms. In the same way that HTTP requests (WebSockets are just about the same thing) become 'hot', I believe the latency will diminish (all other things being equal) but it will still exist.
No, WebSockets are via TCP, so they'll be in order. UDP transport is fire-and-forget, it doesnt send any notification of receipt and it doesnt regenerate the packets using timing information, so you can send messages faster but can make no assumptions regarding receipt or order or events. Page impressions would be a great example of where you dont care really in what order and you probably dont care too much about when the server receives such a message, WebRTC may bring UDP connections between JS and server but the standard is still emerging. For now, WebSockets connect via an HTTP upgrade, meaning they are TCP, where order information and receipt confirmation is a thing (larger and more messages being sent to and fro).
Same answer! It all happens over TCP so the whole trip is a round-trip, but order is assured.
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.