Is there a way to get notified if a certain send() has finished? As i noticed the send() function is not blocking and the code continuous. Is there a simple way to either make it blocking or getting somehow notified if the send is finished?
You could rely on Socket.bufferedamount (never tried)
http://www.whatwg.org/specs/web-apps/current-work/multipage/network.html#dom-websocket-bufferedamount
var socket = new WebSocket('ws://game.example.com:12010/updates');
socket.onopen = function () {
setInterval(function() {
if (socket.bufferedAmount == 0){
// Im' not busy anymore - set a flag or something like that
}
}, 50);
};
Or implement an acknowledge answer from the server for every client message (tried, works fine)
Related
I'm working with HTML5 socket functions to establish a socket connection to my server. HTML5 has functions below to handle disconnecting
Socket.onclose = function()
{
...
}
Socket.onerror = function()
{
...
}
My problem is, how try for reconnect after onclose function executes? I tried to put a while loop inside of it like
ws.onclose = function()
{
While(conn==0)
{
ws = new WebSocket("ws://example.com");
}
}
and
ws.onopen = function()
{
conn=1;
...
}
But did't work.
Any idea?
Here's the script that comes with the Plezi websocket framework... It's fairly basic, but it works on the browsers I used it on (Safari, Chrome and FireFox).
The trick is to leverage the onclose method WITHOUT a loop.
The onclose method will be called even if the websocket never opened and the connection couldn't be established (without calling onopen).
Initiating a reconnect within an onclose is enough.
Writing a loop or a conditional review will not only fail, but will halt all the scripts on the page. Allow me to explain:
Javascript is single threaded. Again: it's an even/task based, single threaded, environment.
This means that your code acts like an atomic unit - nothing happens and nothing changes until your code finished running it's course.
Because connections could take a while to establish, the new WebSocket was designed (and rightfully so) as an asynchronous function.
This is how come you can define the onopen event callback AFTER the creation of the event.
The new websocket connection will be attempted only once the current task/event is finished...
...so a loop will get you stuck forever waiting for a task that can't be performed until your code stops running...
Back to the issue at hand, here's the code. If you have any ideas for improvements, please let me know:
// Your websocket URI should be an absolute path. The following sets the base URI.
// remember to update to the specific controller's path to your websocket URI.
var ws_controller_path = window.location.pathname; // change to '/controller/path'
var ws_uri = (window.location.protocol.match(/https/) ? 'wss' : 'ws') + '://' + window.document.location.host + ws_controller_path
// websocket variable.
var websocket = NaN
// count failed attempts
var websocket_fail_count = 0
// to limit failed reconnection attempts, set this to a number.
var websocket_fail_limit = NaN
// to offer more or less space between reconnection attempts, set this interval in miliseconds.
var websocket_reconnect_interval = 250
function init_websocket()
{
if(websocket && websocket.readyState == 1) return true; // console.log('no need to renew socket connection');
websocket = new WebSocket(ws_uri);
websocket.onopen = function(e) {
// reset the count.
websocket_fail_count = 0
// what do you want to do now?
};
websocket.onclose = function(e) {
// If the websocket repeatedly you probably want to reopen the websocket if it closes
if(!isNaN(websocket_fail_limit) && websocket_fail_count >= websocket_fail_limit) {
// What to do if we can't reconnect so many times?
return
};
// you probably want to reopen the websocket if it closes.
if(isNaN(websocket_fail_limit) || (websocket_fail_count <= websocket_fail_limit) ) {
// update the count
websocket_fail_count += 1;
// try to reconect
setTimeout( init_websocket, websocket_reconnect_interval);
};
};
websocket.onerror = function(e) {
// update the count.
websocket_fail_count += 1
// what do you want to do now?
};
websocket.onmessage = function(e) {
// what do you want to do now?
console.log(e.data);
// to use JSON, use:
// var msg = JSON.parse(e.data); // remember to use JSON also in your Plezi controller.
};
}
// setup the websocket connection once the page is done loading
window.addEventListener("load", init_websocket, false);
I noticed that whenever my server is offline, and i switch it back online, it receives a ton of socket events, that have been fired while server was down. ( events that are ... by now outdated ).
Is there a way to stop socket.io from re-emitting the events after they have not received a response for x seconds ?.
When all else fails with open source libraries, you go study the code and see what you can figure out. After spending some time doing that with the socket.io source code...
The crux of the issue seems to be this code that is here in socket.emit():
if (this.connected) {
this.packet(packet);
} else {
this.sendBuffer.push(packet);
}
If the socket is not connected, all data sent via .emit() is buffered in the sendBuffer. Then, when the socket connects again, we see this:
Socket.prototype.onconnect = function(){
this.connected = true;
this.disconnected = false;
this.emit('connect');
this.emitBuffered();
};
Socket.prototype.emitBuffered = function(){
var i;
for (i = 0; i < this.receiveBuffer.length; i++) {
emit.apply(this, this.receiveBuffer[i]);
}
this.receiveBuffer = [];
for (i = 0; i < this.sendBuffer.length; i++) {
this.packet(this.sendBuffer[i]);
}
this.sendBuffer = [];
};
So, this fully explains why it buffers all data sent while the connection is down and then sends it all upon reconnect.
Now, as to how to prevent it from sending this buffered data, here's a theory that I will try to test later tonight when I have more time.
Two things look like they present an opportunity. The socket notifies of the connect event before it sends the buffered data and the sendBuffer is a public property of the socket. So, it looks like you can just do this in the client code (clear the buffer upon connect):
// clear previously buffered data when reconnecting
socket.on('connect', function() {
socket.sendBuffer = [];
});
I just tested it, and it works just fine. I have a client socket that sends an increasing counter message to the server every second. I take the server down for 5 seconds, then when I bring the server back up before adding this code, all the queued up messages arrive on the server. No counts are missed.
When, I then add the three lines of code above, any messages sent while the server is down are not sent to the server (technically, they are cleared from the send buffer before being sent). It works.
FYI, another possibility would be to just not call .emit() when the socket is not connected. So, you could just create your own function or method that would only try to .emit() when the socket is actually connected, thus nothing would ever get into the sendBuffer.
Socket.prototype.emitWhenConnected = function(msg, data) {
if (this.connected) {
return this.emit(msg, data);
} else {
// do nothing?
return this;
}
}
Or, more dangerously, you could override .emit() to make it work this way (not my recommendation).
Volatile events are events that will not be sent if the underlying connection is not ready (a bit like UDP, in terms of reliability).
https://socket.io/docs/v4/emitting-events/#volatile-events
socket.volatile.emit("hello", "might or might not be received");
We have an SSE (Server-Sent Events) connection open in JavaScript which can time to time get closed, either because of server restarts or other causes. In that case it would be good to reestablish the connection. How to do it? Is there a way to find out on the client side that the connection was closed?
Here: https://developer.mozilla.org/en-US/docs/Web/API/EventSource I found only a way to close the connection, but no callback or a test method for determining whether the connection is still alive.
Thank you for your help.
If the connection is closed (in such a way that the browser can realize it), it will auto-connect. And it tends to do this quickly (default is 3 seconds in Chrome, 5 seconds in Firefox). readyState will be CONNECTING (0) while it is doing this. It is only ever CLOSED (2) if there was some problem connecting in the first place (e.g. due to a CORS issue). Once CLOSED, it does not retry.
I prefer to add a keep-alive mechanism on top, as the browser cannot always detect dead sockets (not to mention a remote server process that is locked up, etc.). See ch.5 of Data Push Apps with HTML5 SSE for detailed code, but basically it involves having the server send a message every 15 seconds, then a JavaScript timer that runs for 20 seconds, but is reset each time a message is received. If the timer ever does expire, we close the connection and reconnect.
EventSource API Update
EventSource API now has three event handlers:
onerror
onmessage
onopen
These should be enough to handle everything you need on the client side.
Something like this:
const ssEvent = new EventSource( eventUrl );
ssEvent.onopen = function (evt) {
// handle newly opened connection
}
ssEvent.onerror = function (evt) {
// handle dropped or failed connection
}
ssEvent.onmessage = function (evt) {
// handle new event from server
}
Ref: mozilla.org : EventSource : Event handlers
Browser support for EventSource API: onopen - caniuse.com
Check readyState property:
var es = new EventSource();
// Сheck that connection is not closed
es.readyState !== 2;
// or
es.readyState !== EventSource.CLOSED;
It is best not to try to determine if the connection was closed. I do not think there is a way to do it. Server Side Events work differently in all of the browsers, but they all close the connection during certain circumstances. Chrome, for example, closes the connection on 502 errors while a server is restarted. So, it is best to use a keep-alive as others suggest or reconnect on every error. Keep-alive only reconnects at a specified interval that must be kept long enough to avoid overwhelming the server. Reconnecting on every error has the lowest possible delay. However, it is only possible if you take an approach that keeps server load to a minimum. Below, I demonstrate an approach that reconnects at a reasonable rate.
This code uses a debounce function along with reconnect interval doubling. It works well, connecting at 1 second, 4, 8, 16...up to a maximum of 64 seconds at which it keeps retrying at the same rate.
function isFunction(functionToCheck) {
return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}
function debounce(func, wait) {
var timeout;
var waitFunc;
return function() {
if (isFunction(wait)) {
waitFunc = wait;
}
else {
waitFunc = function() { return wait };
}
var context = this, args = arguments;
var later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, waitFunc());
};
}
// reconnectFrequencySeconds doubles every retry
var reconnectFrequencySeconds = 1;
var evtSource;
var reconnectFunc = debounce(function() {
setupEventSource();
// Double every attempt to avoid overwhelming server
reconnectFrequencySeconds *= 2;
// Max out at ~1 minute as a compromise between user experience and server load
if (reconnectFrequencySeconds >= 64) {
reconnectFrequencySeconds = 64;
}
}, function() { return reconnectFrequencySeconds * 1000 });
function setupEventSource() {
evtSource = new EventSource(/* URL here */);
evtSource.onmessage = function(e) {
// Handle even here
};
evtSource.onopen = function(e) {
// Reset reconnect frequency upon successful connection
reconnectFrequencySeconds = 1;
};
evtSource.onerror = function(e) {
evtSource.close();
reconnectFunc();
};
}
setupEventSource();
I am implementing a successful long polling within PHP/Node.js application. I have created a routine to launch the long polling AJAX request after the waking up of the computer (after sleep mode) as below.
The problem is that the AJAX request fails due to internet connectivity as it needs some time to get ready and this leads AJAX request to fail. I need to recall this request again until the internet is back but I can't find any way to know if the previous request has failed to send new one and track its status.
I am not using the Jquery and I don't want to use it.
I am able to create a timeout for direct AJAX calls if they don't reach a server within a timeout seconds, but the long polling request status is pending at server for 40 seconds and I need to detect if it fails after 2 seconds from sending.
Is there any solution to do with xmlHTTP object?
I would greatly appreciate your help. Thanks.
var program ={
init: function(){
this.isSleep = function(lastTime){
var lastTime = lastTime;
clearTimeout(program.tt)
program.tt = setTimeout(function(){
var currentTime = new Date().getTime();
if(currentTime > (lastTime + 2000*2) ){
// request fails if the internet connection was not ready
ajax.call({ // long polling request......});
}
program.isSleep(new Date().getTime());
}, 2000);
};
this.isSleep(new Date().getTime());
}
}
Set a variable to true, then call a timer for 2 seconds. If the ajax returns turn that variable to false, if the timer is fired then check your variable and do whatever you need to do.
I am using JavaScript EventSource in my project front-end.
Sometimes, the connection between the browser and the server fails or the server crashes. In these cases, EventSource tries to reconnect after 3 seconds, as described in the documentation.
But it tries only once. If there is still no connection, the EventSource stops to try reconnection and the user have to refresh the browser window in order to be connected again.
How I can prevent this behavior? I need the EventSource to try reconnecting forever, not only once.
The browser is Firefox.
I deal with this by implementing a keep-alive system; if the browser reconnects for me that is all well and good, but I assume sometimes it won't work, and also that different browsers might behave differently.
I spend a fair few pages on this in chapter five of my book (Blatant plug, find it at O'Reilly here: Data Push Applications Using HTML5 SSE), but if you want a very simple solution that does not require any back-end changes, set up a global timer that will trigger after, say, 30 seconds. If it triggers then it will kill the EventSource object and create another one. The last piece of the puzzle is in your event listener(s): each time you get data from the back-end, kill the timer and recreate it. I.e. as long as you get fresh data at least every 30 seconds, the timer will never trigger.
Here is some minimal code to show this:
var keepAliveTimer = null;
function gotActivity(){
if(keepaliveTimer != null)clearTimeout(keepaliveTimer);
keepaliveTimer = setTimeout(connect, 30 * 1000);
}
function connect(){
gotActivity();
var es = new EventSource("/somewhere/");
es.addEventListener('message', function(e){
gotActivity();
},false);
}
...
connect();
Also note that I call gotActivity() just before connecting. Otherwise a connection that fails, or dies before it gets chance to deliver any data, would go unnoticed.
By the way, if you are able to change the back-end too, it is worth sending out a blank message (a "heartbeat") after 25-30 seconds of quiet. Otherwise the front-end will have to assume the back-end has died. No need to do anything, of course, if your server is sending out regular messages that are never more than 25-30 seconds apart.
If your application relies on the Event-Last-Id header, realize your keep-alive system has to simulate this; that gets a bit more involved.
In my experience, browsers will usually reconnect if there's a network-level error but not if the server responds with an HTTP error (e.g. status 500).
Our team made a simple wrapper library to reconnect in all cases: reconnecting-eventsource. Maybe it's helpful.
Below, I demonstrate an approach that reconnects at a reasonable rate, forever.
This code uses a debounce function along with reconnect interval doubling. During my testing, it works well. It connects at 1 second, 4, 8, 16...up to a maximum of 64 seconds at which it keeps retrying at the same rate.
function isFunction(functionToCheck) {
return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}
function debounce(func, wait) {
var timeout;
var waitFunc;
return function() {
if (isFunction(wait)) {
waitFunc = wait;
}
else {
waitFunc = function() { return wait };
}
var context = this, args = arguments;
var later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, waitFunc());
};
}
// reconnectFrequencySeconds doubles every retry
var reconnectFrequencySeconds = 1;
var evtSource;
var reconnectFunc = debounce(function() {
setupEventSource();
// Double every attempt to avoid overwhelming server
reconnectFrequencySeconds *= 2;
// Max out at ~1 minute as a compromise between user experience and server load
if (reconnectFrequencySeconds >= 64) {
reconnectFrequencySeconds = 64;
}
}, function() { return reconnectFrequencySeconds * 1000 });
function setupEventSource() {
evtSource = new EventSource(/* URL here */);
evtSource.onmessage = function(e) {
// Handle even here
};
evtSource.onopen = function(e) {
// Reset reconnect frequency upon successful connection
reconnectFrequencySeconds = 1;
};
evtSource.onerror = function(e) {
evtSource.close();
reconnectFunc();
};
}
setupEventSource();