I have recently developed a web app using PeerJS, and am trying to add reconnect functionality.
Basically, my app works by someone creating a server that clients then connect to. The server person can control what the hosts are doing but its basic two way communication.
If a client disconnects they simply reconnect and it works normally. However if the server user refreshes the page, or their computer crashes then they need to be able to re-establish control over the clients.
The start of this is by regaining the original connection ID and peer api ID, which is fine and easy as they are stored in a database and assigned a unique ID the server user can use to query them. Then to enable the client to reconnect I do this upon close:
// connection is closed by the host involuntarily...
conn.on('close', function() {
// if the clients connection closes set up a reconnect request loop - when the host takes back control
// the client will auto reconnect...
connected = false;
conn = null;
var reconnect_timer = setInterval(function () {
console.log('reconnecting...'); // make a fancy animation here...
conn = peer.connect(connectionid, {metadata: JSON.stringify({'type':'hello','username':username})});
// upon connection
conn.on('open', function() { // if this fails need to provide an error message... DO THIS SOON
// run the connect function...
connected = true;
connect(conn);
});
// didnt connect yet
conn.on('error', function(err) {
connected = false;
});
if(connected === true) {
clearInterval(reconnect_timer);
}
}, 1000);
});
This appears to work, as on the server end the client looks like they have reconnected - the connect function has fired etc. However messages cant be sent between, and the client console says:
Error: Connection is not open. You should listen for the `open` event before sending messages.(…)
Where the 'open' event is shown as having been listened to above...
I hope this is clear - any help is appreciated :)
So in the end to create an auto reconnect script, I simply dealt with the client end of things, ensuring the server was set to the same api_key (for cloudservers) and key:
peer = new Peer(return_array.host_id, {key: return_array.api_key});
and then having the client, upon connection closing:
// connection is closed by the host involuntarily...
conn.on('close', function() {
// if the clients connection closes set up a reconnect request loop - when the host takes back control
// the client will auto reconnect...
peer.destroy(); // destroy the link
connected = false; // set the connected flag to false
conn = null; // destroy the conn
peer = null; // destroy the peer
// set a variable which means function calls to launchPeer will not overlap
var run_next = true;
// periodically attempt to reconnect
reconnect_timer = setInterval(function() {
if(connected===false && run_next===true) {
run_next = false; // stop this bit rerunning before launchPeer has finished...
if(launchPeer(false)===true) {
clearInterval(reconnect_timer);
} else run_next == true;
}
}, 1000);
});
Where launch peer will attempt to launch a new peer. To ensure continuity the new id from the client replaces the old id from the client and everything is a smooth takeover. The hardest part in the end was having the "setInterval" only fire once which is achieved (badly...) through use of boolean flags.
Thanks to anybody who read and thought how they could help :)
Related
I have an ASP.NET Core server, which is using SignalR to generate HTML pages dynamically at runtime through JavaScript. After i shut my application down, in the console i can read that SignalR is disconnected:
signalr.js:2404 Error: Connection disconnected with error 'Error: Websocket closed with status code: 1006 ()'.
The question is - what do i need to do to setup SignalR to wait incoming connection again after disconnecting? Meaning that when my application will be up again, preloaded localhost page will connect automatically and continue working with the new instance of my app.
The way i initialize SignalR in JS:
var connection = new signalR.HubConnectionBuilder()
.withUrl("/foo")
.build();
And then
connection.on("state", function (state) {
// some business logics there
});
connection.start().catch(function err() {
return console.error(err.ToString());
});
As far as I know, Signalr Client library provide the auto reconnect feature. You could enable it by using the withAutomaticReconnect. Please notice: Without any parameters, WithAutomaticReconnect configures the client to wait 0, 2, 10, and 30 seconds respectively before trying each reconnect attempt. After four failed attempts, it stops trying to reconnect.
Also you could write codes to do Manually reconnect. More details, you could refer to this article.
Brando's answer lead me to do the following thing:
var connection = new signalR.HubConnectionBuilder()
.withUrl("/foo")
.build();
function start() {
connection.start().catch(function () {
setTimeout(function () {
start();
//there we can process any HTML changes like
//disabling our loader etc. start() will fail
//with exception if there's no server started
//and it will try again in 5 seconds
}, 5000);
});
}
connection.onclose(e => {
//process here any HTML changes like
//showing the loader, etc, an then
start();
})
connection.on("foo", function (state) {
// some business logics there
});
start();
It looks like typical relationship between ASP.NET and signalR + vanilla javascript
With the following code i open a stream connection to the Binance crypto exchange:
let adress = 'wss://stream.binance.com:9443/ws/btcusdt#kline_1h';
const ws = new WebSocket(adress);
If i make this call for different crypto currencys then i have later a few streams open, i want to know how can i check the current open streams, is there something like a function or parameter where i can see for which currencys i have a open stream running?
because i also have the problem that it looks like streams are stopping sometimes and i dont have a good solution for checking which streams have stop and how to receonnect them. My idea is now to first find a way how to check which streams are running and then maybe if one streams is stop i will just send the connection request again.
In javascript there is a websocket events thing, so all you need is to
ws.onclose = function(event) {
ws = new WebSocket(adress);
//just reopen it
};
Or, for more safety, you can
ws.onclose = function(event) {
if (event.wasClean) {
ws = new WebSocket(adress);
} else {
console.log('Connection error!');
//or whatever u want
}
};
Sorry for this stupid styling, I'm newbie there
If you have your ws variable, then checking whether the websocket is open and alive is done with
if(ws && ws.readyState === 1){
// is opened
}
For other states of the websocket, see the docs.
If you want to receive push messages from the server, you need to keep the ws connection open. If not, you can close the ws after a query and reopen it then for another query. You should wait for the closed state ws.readyState === 3 before reopening.
If you need to keep all ws connections open, then you need a list of ws Objects. You push new objects to the list:
let ws_list = [] // global list of ws objects
let create_connection = function(url){
try{
ws_list.push(new WebSocket(url));
} catch(err){
console.log(err, url);
}
}
let do_something = function(){
for(let ws of ws_list){
// do something with the ws object
}
}
I have a socket.io connected to a https server listening for javascript to emit information to it. I am finding that when I refresh, the socket is recording the connection - so if i console.log something in the on('connection', function(socket) { } part of the code, I will see a response in the console.
What I am finding though, is that on the server side, the socket.on() calls are not picking up anything.
So if I have a line in Javascript which is socket.emit('ping', msg), 'ping' will work on the server side for the first 9-10 refreshes, and then on the 11th refresh it will stop working - and it will stop working on all devices and all browsers. So if I open another browser and try to load it up, it won't work either.
I have checked to see if there are multiple connections building up or something like that, but I am not seeing anything. Any idea why this weird behaviour is happening?
Server Side
io.use(function(socket, next) {
// Authentication of the user is happening here - checking for cookies, sessions, etc.
}).on('connection', function(socket) {
// Setting up some variables and checking other stuff
var socketID = socket["id"];
var username = socketUsers[socketID];
locked[socketID] = {};
// Checking for the username
if(username == '' || typeof username == "undefined") {
// Emit some stuff back to Javascript
io.to(`${socketID}`).emit('criticalError', true);
return false;
}
else {
// We have a valid login, so connect to the server
++connected;
// This line will console log every time I refresh
console.log("Some text");
socket.on('ping', function(msg) {
// This line will console log for the first 9-10 refreshes, and then it suddenly stops working. Any ideas why?
console.log('Server was pinged by ' + username);
});
// This line will console log every time I refresh
console.log("Some other text");
}
});
Client side
var socket = io('https://example.com:1337');
$(document).ready(function() {
socket.emit('ping', true);
});
This is an interesting problem that you are facing. There are multiple points to check on - However the first problem I would suggest is with the below code:
var socket = io('https://example.com:1337');
$(document).ready(function() {
socket.emit('ping', true);
});
If this is the exact code on the client side, chances are that the socket.emit is getting called before the connection handshake goes through. This can be random as sometimes the document ready event might be delayed. Try setting a timeout for the emit event -
var socket = io('https://example.com:1337');
$(document).ready(function() {
setTimeout(function(){socket.emit('ping', true);},2000);
});
and give it a try. If this works, you found the problem. Next step is to ensure authentication is working correctly - Without the complete code, advising on this is difficult. Feel free to edit the question and share the code if your issue isn't resolved.
I currently forced to create a new RabbitMQ connection every time a user loads a page on my website.
This is creating a new TCP connection every time. However, i'm trying to reduce the number of TCP connections i make to Rabbit with the NodeJS AMQP plug in. Here is what i have:
var ex_conn = get_connection(uri); //http:rabbitm.com
if(ex_conn == false) {
var tempConn = amqp.createConnection({
url: uri
});
connections.push({
host: uri,
obj: tempConn
});
}
else {
var tempConn = ex_conn.obj;
}
The issue i'm running into is that if i try to do:
tempConn.on('ready', function() {
});
Then the ready function does not get triggered. I'm assuming, that is because the ready call back was already defined and it is not going to be re triggered. What i'm looking to do is bind a new queue by doing:
tempConn.queu('', {});
Any thoughts on how to get around this issue is much appreciated.
thanks.
With NodeJS and websockets, I am trying to implement an online game server, for a multiplier game, I have people moving around with the ability to see each other, but when the server spawns a new enemy, I want to be able to broadcast the message to all connected clients.
But without the clients messaging the server first.
For example - The game spawns an enemy, send information to all clients.
My code so far:
/**************************************************
** NODE.JS REQUIREMENTS
**************************************************/
var util = require("util"),
// Utility resources (logging, object inspection, etc)
io = require("socket.io"),
// Socket.IO
Player = require("./Player").Player, // Player class
Zombie = require("./Zombie").Zombie;
// Socket controller
var socket,
// Array of connected players
players,
zombies;
var level = 1,
score = 0,
levelling = false;
function init() {
// Create an empty array to store players
players = [];
zombies = [];
// Set up Socket.IO to listen on port 8000
socket = io.listen(8000);
// Configure Socket.IO
socket.configure(function() {
// Only use WebSockets
socket.set("transports", ["websocket"]);
// Restrict log output
socket.set("log level", 2);
});
// Start listening for events
setEventHandlers();
};
blah blah blah -- tl;dr -- Game loop:
function mainLoop(){
//Main loop for the server. Called 60 times a second. May be modified for game
speed.
//setTimeout(mainLoop, 1000/5000);
this.broadcast.emit("update");
//THIS broadcast.emit line is where the error is being thrown. I need to know how
to broadcast here without the client initializing first.
};
I have tried a few things, socket.broadcast.emit("update");, but with no success.
I have tried for looping through currently known users and using "this.emit" but I always get "cannot call method emit of undefined" so damn annoying.
but when I have the clients send a message first, for example, when the client moves, it sends some information to the server, the server reacts with:
function onSocketConnection(client) {
util.log("New player has connected: " + client.id);
// Listen for client disconnected
client.on("disconnect", onClientDisconnect);
// Listen for new player message
client.on("new player", onNewPlayer);
// Listen for move player message
client.on("move player", onMovePlayer);
//listen for deaths
client.on("death", onPlayerDeath);
//listen for not moved message, to stop sprite animation
client.on("not moved", notMoved);
};
// Player has moved
function onMovePlayer(data) {
// Find player in array
var movePlayer = playerById(this.id);
// Player not found
if (!movePlayer) {
util.log("Player not found: "+this.id + " when asking if moved");
return;
};
// Update player position
movePlayer.setX(data.x);
movePlayer.setY(data.y);
movePlayer.setDir(data.dir);
// Broadcast updated position to connected socket clients
this.broadcast.emit("move player", {id: movePlayer.id, x:
movePlayer.getX(), y:movePlayer.getY(), dir: movePlayer.getDir()});
};
AND THEN the "this.broadcast.emit" has no issues. Without throwing the error.
OK, I solved this annoyance. I will post the answer for anyone else in the same boat. I had to remove the socket variable, and change the io require call from io = require("socket.io") to:
io = require("socket.io").listen(8000)
With this I then needed to change all corresponding variables which were dependent on the socket variable, for example:
From
socket.sockets.on("connection", onSocketConnection);
To
io.sockets.on("connection", onSocketConnection);
And then broadcasting is fine, for example: io.sockets.emit("updates")