I have a single device establishing two WebSocket connections to the WebSocket server. Whenever the second WebSocket connection sends a message to the server, only the first websocket.on("message" event emitter responds. There is no way to differentiate which WebSocket the message is coming from because there seems to be only a single websocket.on("message" event emitter object.
I apologize if I am overlooking something simple, I am a node.js and coding novice. From the code below it looks like there should be separate event emitter objects created for each WebSocket connection so that the server knows which connection the message is coming from. My code looks like this:
var connections = new Map();
var idCounter = 0;
wss.on("connection", function connection(ws) {
var connectionID = idCounter++;
connections.set(connectionID, ws);
var session = connections.get(connectionID);
session.on("message", function incoming(message) {
session.send(message);
}
}
On your client side you need to use the
{'force new connection':true}
flag in options. Otherwise it reuses existing connections when connecting to the same server.
The client side is an iPad using the Starscream WebSocket library. Each connection is established as a separate object using Swift on the iPad. I have verified that two distinct connections are being established as I will describe next.
Within wss.on("connection", function connection(ws) { } ); I have printed ws to the console and see two distinct WebSocket objects as each one connects (I have compared these with a text editor and they contain unique information between one another). I can also send messages to each distinct connection and receive them as expected on the iPad to the corresponding WebSocket.
Shouldn't the code as I have written it above be creating two separate event emitters when messages are received from two different WebSockets?
@LordMajestros Thank you for your response. I greatly appreciate it.
I'm so sorry, I just realised this was a ws issue not a socket.io issue my answer applies to socket.io.
If things are as you say then the problem may be on the server. I'm not familiar with the starscream socket library.
I have performed another test. With the code below objectTest contains the unique WebSocket connection distinguished by 'sec-websocket-key' printed to the console. However this.send(message); and console.log(this); both refer to the first established WebSocket connection even while objectTestMap contains the second objectTest that is unique.
var connections = new Map();
var idCounter = 0;
wss.on("connection", function connection(ws) {
var connectionID = idCounter++;
connections.set(connectionID, ws);
var session = connections.get(connectionID);
var sendThis = String(connectionID);
session.send(sendThis);
var objectTestMap = new Map();
var objectTest = session.on("message", function incoming(message) {
this.send(message);
console.log(this);
});
objectTestMap.set(connectionID, objectTest);
console.log(objectTestMap.get(connectionID));
});
Why are you passing the connection through to a session handler and grabbing it from the handler before using it within the same call? Seems like unnecessary work. The issue is most likely with your session handling code and not with WS. Have you tried using ws.on('message' instead of session.on('message'?
@Gray1989 Thanks for the input. I actually started out using ws.on('message' and this is when I discovered the behavior. I added the session handler for testing purposes so that I could track and print the session object to the console at different parts of the code. I wanted to be sure that I was sure a separate object was being created and used at ws.on('message'.
Below is a snippet of another test that I performed.
session.on("message", function incoming(message) {
console.log(session); // This prints the first object that was created even when executed by the second WebSocket connection
});
I am beginning to think that WS has a problem differentiating connections from the same source. Out of the very few resources for WS I have not yet been able to find much information about the server differentiating the connections. I have only found mention of saving the WebSocket connections and sending messages to particular connections from the server, which has always worked fine for me.
I am trying to better understand how WS handles ws.on('message', but I easily get lost through all of the node.js coding patterns.
I did a test of my own to narrow down the cause of the issue. It appears as though there is no issue with the WS module. My test uses the following code on the server-side:
var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
this.send(message);
console.log(this.upgradeReq.headers['sec-websocket-key']);
});
});
I then ran the following code on the client side:
var ex = [];
ex.push(function(){
var exampleSocket = new WebSocket("ws://localhost:8080/");
exampleSocket.onopen = function (event) {
console.log('Connection Open');
exampleSocket.send('This is a test');
};
exampleSocket.onmessage = function (event) {
console.log(event.data);
};
return exampleSocket;
}());
ex.push(function(){
var exampleSocket = new WebSocket("ws://localhost:8080/");
exampleSocket.onopen = function (event) {
console.log('Connection Open');
exampleSocket.send('This is a test 2');
};
exampleSocket.onmessage = function (event) {
console.log('Test2: '+event.data);
};
return exampleSocket;
}());
Which yielded the following results on the client-side
Connection Open
This is a test
Connection Open
Test2: This is a test 2
This test also logged different sec-websocket-key values to the console on the server-side, which means that it is performing exactly as expected.
@Gray1989 I have replicated your code and got the exact same result as you, but when I connect using Swift/Starscream on the iPad I run into the problem. So it seems to be an issue on the iPad side of things at this point. I will do some further troubleshooting and post the results.
Thanks a bunch for helping me along!
I have resolved the problem and there was nothing wrong with WS. Thanks everyone for the help!
Hi
I have the same issue . I am trying to implement a web chat using websocket and Er-lang. The whole set up of mine worked well . But now a facing an issue like the connection is getting replaced by latest connection. I'm not able to distinguish between the user. I have implemented in node js.
My code is below
This is the server side code
wss.on('connection', function (ws) {
function init(user,pass,loginStatus,id) {
//To initiate the xmpp connection
wsURI = "ws://52.77.212.110:5223/xmpp";
//create a new WebSocket object.
wsss = new webSocket(wsURI, [ 'xmpp' ]);
wsss.onopen = function(evt) {
if(loginStatus===true){
login(user, pass,loginStatus);
}
else{
wsss.close(3000,"Deliberate Connection");
}
};
wsss.onclose = function(evt) {
var code=evt.code;
console.log('connection closed');
if(code!==3000){
if(loginStatus===true){
console.log('connection started');
login(user, pass,loginStatus);
}
}
};
wsss.onmessage = function(evt) {
processResponse(evt.data, user, domain,id);
};
wsss.onerror = function(evt) {
console.log('Error Occured');
console.log(evt);
};
this.wsss = wsss;
//
}
}
The client side code
var url = "ws://127.0.0.1:8087/";
socket = new WebSocket(url);
socket.onopen = function () {
var user=document.getElementById("user").value;
var secretCode=document.getElementById("secret-code").value;
setCookie("username", user, 365);
setCookie("user_id", getRandomInt(0, 10000), 365);
var bool=true;
//To set the username in session
var message = {};
message.user = user.trim();
message.pass = secretCode.trim();
message.id = getCookie("user_id");
message.type = "login";
message.connection=bool;
var msg=JSON.stringify(message);
socket.send(encodeURIComponent(msg));
};
@peradym What was the problem causing the issue? Would you explain it a little bit? Thanks
Peradym here under a different account. The problem was with my client side code. It's been a while so I can't completely remember the details, but there was nothing wrong with my server side ws implementation.
@tomdaniel0x01 Thanks for your response. I having the same issue in client too. My case is I have multiple namespaces to same server through web sockets, all namespaces connected and able to emit message successfully, however, only one socket/namespace can receive incoming stream data, did you have the same problem? If so, do you still remember to to fix it?
@terenceLuffy I just reviewed my code. My client handles multiple sessions by creating new objects for each session and storing them as a property. The Swift Starscream library provides delegate methods that are called when a websocket receives data from the server. So for each websocket object, I have 'primarySocket.onData' and secondarySocket.onData' methods that are called depending on which actually received the data.
I remember that a simple typo in my client code caused all data to only be received on the primary socket. The typo was something like accidentally overwriting the secondarySocket property with the primarySocket object.
@tomdaniel0x01 I checked my source and didn't find the same cause you had, and my case is more wired, because everything is working fine in iOS 10, but in iOS 8 & 9, it only get one socket working fine for incoming events.
For me it was a scoping issue. Privatizing this function worked:
function createConnectionAndPingServer(key) {
serverList[key].socket = new WebSocket(serverList[key].url + "?" + serverOpts.query);
addSocketMethods(serverList[key].socket);
receivePingHandler(key);
serverList[key].socket.onopen = function() {
serverList[key].socket.emit('ping_from_client', Date.now());
};
}
for (var key in serverList) {
createConnectionAndPingServer(key);
}
The keys in serverList are websocket connection URLs. If createConnectionAndPingServer wasn't a private function, it doesn't work and it's as if Websockets creates the same connection twice instead of two connections.
I can't understand it though. Everything was saved as a reference in an object so I don't understand why it needed to be privatized. If anyone could shed light on this, I'd be grateful.
Most helpful comment
On your client side you need to use the
flag in options. Otherwise it reuses existing connections when connecting to the same server.