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
}
}
Related
I am using solutions provided in following topics to inspect WebSockets traffic (messages) on the web page, which I do not own (solely for learning purposes):
Inspecting WebSocket frames in an undetectable way
Listening to a WebSocket connection through prototypes
https://gist.github.com/maskit/2252422
Like this:
(function(){
var ws = window.WebSocket;
window.WebSocket = function (a, b, c) {
var that = c ? new ws(a, b, c) : b ? new ws(a, b) : new ws(a);
that.addEventListener('open', console.info.bind(console, 'socket open'));
that.addEventListener('close', console.info.bind(console, 'socket close'));
that.addEventListener('message', console.info.bind(console, 'socket msg'));
return that;
};
window.WebSocket.prototype=ws.prototype;
}());
The issue with the provided solutions is that they are listening on only 1 of 3 WebSocket connections ("wss://..."). I am able to see in the console the messages that I receive or send, but only for one connection.. Is there something I am missing? Is it possible that two other service are any different and prohibiting the use of prototype extension technique?
p.s. I will not provide an URL to the web resource that I am doing my tests on, in order to avoid possible bans or legal questions.
Okay, since it's been weeks and no answers, then I will post a solution which I ended up using.
I have built my own Chrome extension that listens to WebSocket connections and forwards all requests and responses to my own WebSocket server (which I happen to run in C#).
There are some limitations to this approach. You are not seeing the request header or who is sending the packets.. You are only able to see the payload and that is it. Also you are not able to modify the contents in any way or send your own requests (remember - you have no access to header metadata). Naturally, another limitation is that you have to be running Chrome (devtools APIs are used)..
Some instructions.
Here is how you attach debugger to listen to network packets:
chrome.debugger.attach({ tabId: tabId }, "1.2", function () {
chrome.debugger.sendCommand({ tabId: tabId }, "Network.enable");
chrome.debugger.onEvent.addListener(onTabDebuggerEvent);
});
Here is how you catch them:
function onTabDebuggerEvent(debuggeeId, message, params) {
var debugeeTabId = debuggeeId.tabId;
chrome.tabs.get(debugeeTabId, function (targetTab) {
var tabUrl = targetTab.url;
if (message == "Network.webSocketFrameSent") {
}
else if (message == "Network.webSocketFrameReceived") {
var payloadData = params.response.payloadData;
var request = {
source: tabUrl,
payload: params.response.payloadData
};
websocket.send(JSON.stringify(request));
}
});
}
Here is how you create a websocket client:
var websocket = new WebSocket("ws://127.0.0.1:13529");
setTimeout(() => {
if (websocket.readyState !== 1) {
console.log("Unable to connect to a WebsocketServer.");
websocket = null;
}
else {
console.log("WebsocketConnection started", websocket);
websocket.onclose = function (evt) {
console.log("WebSocket connection got closed!");
if (evt.code == 3001) {
console.log('ws closed');
} else {
console.log('ws connection error');
}
websocket = null;
};
websocket.onerror = function (evt) {
console.log('ws normal error: ' + evt.type);
websocket = null;
};
}
}, 3000);
Creating the server is outside the scope of this question. You can use one in Node.js, C# or Java, whatever is preferable for you..
This is certainly not the most convenient approach, but unlike java-script injection method - it works in all cases.
Edit: totally forgot to mention. There seems to be another way of solving this, BUT I have not dig into that topic therefore maybe this is false info in some way. It should be possible to catch packets on a network interface level, through packet sniffing utilities. Such as Wireshark or pcap. Maybe something I will investigate further in the future :)
The setup
I am using the websockets/ws library to listen to a WSS feed. It works well, it's lightweight enough and it seems to be one of the fastest around (which is important).
I'm trying to differentiate between me programmatically closing the connection, and them closing it for whatever reason.
According to the docs, I can send a code (int) and a reason (string), both of which are sent to the on close event. But by all accounts, this functionality no longer exists.
Tests
Most codes throw a First argument must be a valid error code number error
Leaving it blank sends code = 1005 and an empty reason to the event
If I enter a code of 1005, I get the invalid error
If I enter a code of 1000, the event receives code = 1006 and still an empty reason (regardless of what I put)
^ tests are simple enough...
var WebSocket = require('ws');
var ws = new WebSocket(url);
ws.on('close', function(code, reason) {
console.log(code);
console.log(reason);
});
var code = 1234,
reason = 'whatever reason';
setTimeout(function() {
ws.close(code, reason);
}, 5000);
But...
I need to be able to tell if I've closed the connection, or if it was closed for another reason (connection lost, they closed it because of time limits, etc). Depending on the reason it was closed, I sometimes need to immediately reopen the connection, but only sometimes.
I can do something like...
_initWS(url) {
var ws = new WebSocket(url);
ws.on('open', function() {...});
ws.on('close', function(code, reason) {
ws = null; // Don't know if needed
ws = _initWS(url); // Reopen the connection, for ever and ever...
});
ws.on('message', funciton(msg) {...});
return ws;
}
var someFeed = _initWS(someURL);
... but since the code and reason are all but meaningless, this automatically restarts the connection regardless of why it was closed. This is great if the connection was lost or timed-out, but not so great if I want to close it programmatically...
someFeed.close(); // Connection immediately reopens, always and forever
Question(s)
How can I differentiate between different closes? I don't want to change the library code, because then I can't use npm install when I move my own code around. Can I override the close method within my own code?
Or is there an equally lightweight and lightning-fast library that has the functionality I'm looking for? I just need to be able to reliably send a unique code and/or reason, so I know when I'm trying to close it manually. I know that asking for recommendations is taboo, but there are too many libraries to test each one, and no benchmarks that I can find.
From the suggestion from #Bergi
_initWS(url) {
var ws = new WebSocket(url);
ws.didIClose = false; // This is the flag to set if I close it manually
ws.forceClose = function() {
ws.didIClose = true;
ws.close();
};
ws.on('open', function() {...});
ws.on('close', function(code, reason) {
if(!ws.didIClose) { // If I didn't close it, reopen
ws = null;
ws = _initWS(url);
}
});
ws.on('message', funciton(msg) {...});
return ws;
}
var someFeed = _initWS(someURL);
And then to use it...
someFeed.didIClose = true;
someFeed.close(); // Won't reopen the connection
Or, a bit cleaner...
someFeed.forceClose();
Edit #1
Modified solution to include the forceClose() method - requiring only one clean line of code to programmatically close it (instead of two).
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 :)
This is my server side websocket script:
var clients = [ ];
//sample request: ****:8080/?steamid=123456789
var connection;
var aqsteamid = getParameterByName("steamid",request.resource);
connection = request.accept(null, request.origin);
connection.ID = aqsteamid;
connection.balRefreshes = 0;
connection.clientIndex = clients.push(connection) - 1;
//check if this user is already connected. If yes, kicks the previous client ***====EDITED====***
for(var i = 0; i < clients.length; i++)
{
if(clients[i].ID === aqsteamid){
var indx = clients.indexOf(clients[i]);
clients[indx].close();
}
}
console.log('ID',connection.ID,' connected.');
socket.on('close', function(webSocketConnection, closeReason, description){
try{
console.log('ID',webSocketConnection.ID,'disconnected. ('+closeReason+';'+description+')');
webSocketConnection.balRefreshes = 0;
webSocketConnection.spamcheck = false;
clients.splice(webSocketConnection.clientIndex, 1);
}catch(e)
{
console.log(e);
}
});
Basically what I want is to kick all connections with same ID (for example, connecting with multiple browser tabs).
But, instead of kicking the old client, it kicks both clients or in some cases both clients remain connected with same ID.
Is there any other way or is there any mistakes in my script?
Thanks
using an object instad of Array to key the clients pool makes it faster and simpler:
var clients = {};
//sample request: ****:8080/?steamid=123456789
var connection;
var aqsteamid = getParameterByName("steamid",request.resource);
connection = request.accept(null, request.origin);
connection.ID = aqsteamid;
connection.balRefreshes = 0;
clients[aqsteamid]=connection;
socket.on('close', function(webSocketConnection, closeReason, description){
try{
console.log('ID',webSocketConnection.ID,'disconnected. ('+closeReason+';'+description+')');
webSocketConnection.balRefreshes = 0;
webSocketConnection.spamcheck = false;
delete clients[aqsteamid];
}catch(e)
{
console.log(e);
}
});
//check if this user is already connected. If yes, kicks the previous client
if(clients[aqsteamid]) clients[aqsteamid].close();
console.log('ID',connection.ID,' connected.');
With an object pool, we can remove all the array pool looping and comparing logic, and our index will never get out of sync.
It sounds like multiple connections with the same ID could be part of a genuine workflow with multiple tabs (unlike, say, malicious users that intentionally scrape data w/multiple threads...)
Rather than "kicking" the users from other tabs and then having to deal with them re-connecting, a more elegant solution would be to introduce an orchestration layer across multiple tabs.
You can rely on localstorage api to elect a master tab that will handle communications with the server (doesn't really matter if it's websocket or ajax) and share responses with other tabs - again, through localstorage. It doesn't really matter if you have 1 or 20 tabs open when you can share that data since you care about same message notifications, or stock ticker updates, or whatever.
From another stackoverflow answer:
The storage event lets you propagate data between tabs while keeping a
single SignalR connection open (thereby preventing connection
saturation). Calling localStorage.setItem('sharedKey', sharedData)
will raise the storage event in all other tabs (not the caller):
$(window).bind('storage', function (e) {
var sharedData = localStorage.getItem('sharedKey');
if (sharedData !== null)
console.log(
'A tab called localStorage.setItem("sharedData",'+sharedData+')'
);
});
Given the code above, if sharedKey value is already available when the page is loaded, assume a master tab is active and get shared values from localstorage. You can check if a master tab re-election is needed (i.e. that browser tab has been closed or navigated away) with an interval or relying on something more sophisticated like page visibility api.
Note you're not limited to sharing "same" data across multiple tabs but instead batch any requests over a shared channel.
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.