socketIO handshake behavior when connecting to different namespaces - node.js

I am having troubles with socketIO (nodejs) and using multiple namespaces. Maybe I misunderstood how to use it, but this is how I understood it should work.
Server side (nodejs)
io.of('/game').on('connection', socket => {
console.log(socket.handshake.query);
console.log('user connected to game socket', socket.id);
})
io.of('/api').on('connection',socket => {
console.log(socket.handshake.query);
console.log('user connected to api socket', socket.id);
}
Client side (browser)
function test (){
gameSocketTest = io.connect('http://localhost:4003/game',{query:{test:'test'}});
apiSocketTest = io.connect('http://localhost:4003/api');
}
The weird thing is that the query (that I only send to the "game" namespace) arrives in both handlers, also the one of the api.
If I inverse the order of the client side code to:
function test (){
apiSocketTest = io.connect('http://localhost:4003/api');
gameSocketTest = io.connect('http://localhost:4003/game',{query:{test:'test'}});
}
the query in the handshake is empty in both handlers server side. So my conclusion is that one way or another the same handshake is used for both connections (however the socket ids are different for both).
Is it "uberhaupt" possible to do what I am trying to do? If so, what am I not understanding with this weird handshake behavior.
In my understanding calling on the client side "io.connect()" should create 2 separate socket io clients, each with their own handshake.
Thanks for any help!
Andries

You must open new connection explicitly as Socket.IO tries to be smart and re-use connections (which causes a lot of connection issues actually) The way around this is use the force new connection option in your io.connect
gameSocketTest = io.connect('http://localhost:4003/game',{query:{test:'test'}});
apiSocketTest = io.connect('http://localhost:4003/api', {'force new connection': true});
From docs
When called, it creates a new Manager for the given URL, and attempts
to reuse an existing Manager for subsequent calls, unless the
multiplex option is passed with false. Passing this option is the equivalent of passing 'force new connection': true

Related

socket.emit within collection.find.each problem [duplicate]

So, I am still in the experimental phase of Socket.io, but I just can't figure out why my code is doing this. So, I have the code below and when I console.log the code, it repeats the the connection even when there is only one connection. Do you know a solution?
io.on('connnection', (socket) => {
console.log("A new user is connected.")
})
Client side:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io()
</script>
Node.js Console:
A new user is connected.
A new user is connected.
A new user is connected.
A new user is connected.
A new user is connected.
A new user is connected.
A new user is connected.
...
(Note: there is only one connection, and I have already cleared the browser cashe)
Here are some of the possible reasons for socket.io connecting over and over:
Your socket.io client and server versions do not match and this causes a connection failure and an immediate retry.
You are running with some infrastructure (like a proxy or load balancer) that is not configured properly to allow lasting webSocket connections.
You are running a clustered server without sticky webSocket connections.
You have put the server-side io.on('connnection', ...) code inside some other function that is called more than once causing you to register multiple event handlers for the same event so you think you're getting multiple events, but actually you just have multiple listeners for the one occurrence of the event.
Your client code is calling its var socket = io() more than once.
Your client page is reloading (and thus restarting the connection on each reload) either because of a form post or for some other reason.
FYI, you can sometimes learn something useful by installing listeners for all the possible error-related events on both client and server connections and then logging which ones occur and any parameters that they offer. You can see all the client-related error events you can listen to and log here.
To solve repetion problem write your code like that for socket:
io.off("connnection").on('connnection', (socket) => {
console.log("A new user is connected.")
})

Authenticating using ws WebSocket

I'm using ws webSocket (can't use socket.io) and I'm a bit new to it. I know how it works, but don't fully get a couple of things.
How can I authenticate using jwt? The docs say that using the upgrade event is the correct way to go about it, but when the client connected, the code in the upgrade function didn't run.
What's the correct way to add an event type? In socket.io, one can listen using client.on('eventType', function). What's the webSocket ws equivalent?
When and why would one use paths in webSocket ws?
Update
The update event doesn't get fired on my end. Here's the code I have:
File 1
const server = require('http').createServer(app);
require('./socket/socket')(server);
File 2
module.exports = function(server) {
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
server.on('upgrade', function(request, socket, head) {
console.log('Upgraded');
wss.handleUpgrade(request, socket, head, function(ws) {
wss.emit('connection', ws, request);
});
});
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
});
};
How can I authenticate using jwt? The docs say that using the upgrade event is the correct way to go about it, but when the client connected, the code in the upgrade function didn't run.
Well, there are lots of different choices. You could pass a token as a query parameter of a custom header when first making the connection. You could require that the first data sent over the webSocket is a token and disconnect if such a token does not arrive soon and first. If the client was already authenticated and there's an http cookie that indicates that, you could examine that cookie upon websocket connection.
What's the correct way to add an event type? In socket.io, one can listen using client.on('eventType', function). What's the webSocket ws equivalent?
The socket.io message types (or event types as you call them) are something that socket.io adds on top of webSocket. The webSocket protocol itself does not have such a thing. You send a data packet and that data packet arrives at the other end. All data packets are of the same webSocket "type". If you want to identify your data packets as being a certain type of message, then you have to invent a way of doing that inside your data back. For example, if your data was JSON formatted, you could add a type: "someType" property to event message and then examine that property upon receipt (this is essentially what socket.io does although it does it outside of the socket.io payload).
When and why would one use paths in webSocket ws?
You may have two completely separate parts of code that use a webSocket for different things that have nothing in common and reside in separate modules and it's just more straightforward for each to have its own webSocket connection. And, they may be used by two completely separate types of clients.
How to access the orignal URL when a webSocket client connects to your server is shown here on your server's upgrade event used with the ws library.
You will note in that example that the upgrade event occurs on the http server, not on the websocket server instance.
ws provide auth examples.
These work when a HTTP server first handle the auth requests. Then pass a
HTTP upgrade request to ws rather than ws itself listening on a port. Note
the noServer: true option they include in the setup:
const wss = new WebSocket.Server({ clientTracking: false, noServer: true });
The jwt component will be easier using koa or express in HTTP
first then doing the upgrade in this way. Otherwise you would need to
write a ws message handler to look for some token data and verify it.
The message is the event in ws. You get to write anything more specific on top of the message event, which is a big reason to use socket.io that has done all that for you (including client callback functions, which are super helpful).
Using a URI path in the ws:// or http upgrade request would usually be to connect to a separate ws context on the server side. A simple way to namespace or separate connection into discreet groups.

Reuse socket after connect fails in node

I need to reuse socket for two connect calls made using http.request. I tried passing custom agent limiting number of sockets but the first socket is removed before the 2nd connect call is made by code:
https://github.com/nodejs/node/blob/master/lib/_http_client.js#L438
mock code:
var options = {
method: 'CONNECT', agent: new http.Agent({ keepAlive: true, maxSockets: 1 })
};
var request = this.httpModule.request(options);
request.on('connect', (res, sock, head) => {
console.log(sock.address());
// some processing...
var request2 = this.httpModule.request(options);
request2.on('connect', (res, sock, head) => {
console.log(sock.address());
});
request2.end();
});
request.end();
Is there some way by which I can reuse the same socket for two connect calls?
The two unique sockets are required for this form of communication.
Each socket in this case represents a connection between a client and a server. There is no such socket that represents n clients and one server, so to speak. They also don't act like "threads" here, where one socket can perform work for many clients.
By setting the max sockets to 1, you've requested that only 1 client connection be active at any time. When you try to connect that second client, it kills the first one because the max is reached and we need room for a new connection!
If you want to recycle sockets -- For example, a client connects, refreshes the page after an hour, and the same client triggers another connection -- There's probably not a way to do it this high in the technology stack, and it would be far more complicated and unnecessary than destroying the old socket to make way for a new one anyway. If you don't understand why you would or wouldn't need to do this, you don't need to do it.
If you want to send a message to many clients (and you wanted to accomplish it "under one socket" in your question), consider using the broadcast and emit methods.

socket.io client connection cannot be made on the 2nd time

Currently, I am implementing an API using nodejs express, then it needs to connect to socket.io and send event.
The API is located in socket.io-client (client), and it connects to socket.io (server)
1st API call: success
The connection is made for the 1st call of the API, message is sent and socket can be disconnected, with the 'disconnect' callback is invoked both on client and server side.
2nd API call: failure
When the API is invoked the 2nd time, the connection to server cannot be made, 'client' callback on client side is not called.
3rd API call: success
Then I tried to restart the client side, keeping other things unchanged. The API is called again, and the connection to socket.io is made successfully and everything is fine.
Can anyone explain the logistics behind this?
Updated
client.js
App.getByUserId(message.to_id, function(error, app) {
var socket = io.connect('http://127.0.0.1:9002');
socket.on('connect', function(){
console.log("client connect socket id:" + socket.id);
console.log("appkey:" + app.private_token);
socket.emit('appkey.check',{appkey: app.private_token, uuid: message.to_id.uuid}, function(data){
socket.emit("forceDisconnect");
socket = null;
});
});
You just hit one of Socket.IO's many "features" or "bugs" depending how you see this. Socket.IO tries to be smart and re-use connections (which causes a lot of connection issues actually) The way around this is use the force new connection option in your io.connect:
io.connect('http://127.0.0.1:9002', { 'force new connection': true });
What you could also do is use https://github.com/primus/primus which wraps Socket.IO if you use the socket.io transformer. Internally, it completely removes the use of the io.connect and uses the much more lower level io.Socket constructor to create more stable connections that you would get with a stock socket.io.
With socket 1.0+, you have to use this for forcing new connection.
io.connect(SERVER_IP, { 'forceNew': true });

Adding data to a socket.io socket object

I am trying to add some custom information to my socket object on connect, so that when I disconnect the socket, I can read that custom information.
IE:
// (Client)
socket.on('connect', function(data){
socket.customInfo = 'customdata';
});
// (server)
socket.on('disconnect', function () {
console.log(socket.customInfo);
});
Since it is JavaScript you can freely add attributes to any object (just as you did). However socket.io does give you a built-in way to do that (so you won't have to worry about naming conflicts):
socket.set('nickname', name, function () {
socket.emit('ready');
});
socket.get('nickname', function (err, name) {
console.log('Chat message by ', name);
});
Note that this is only on one side (either client or server). Obviously you can't share data between client and server without communication (that's what your example suggests).
The socket in your browser and the socket in the server won't share the same properties if you set them.
Basically you have set the data only at the client side (which is in your browsers memory NOT on the server).
For anyone still looking for an answer, there are a couple of things you can do to share data from the client to the server without actually sending a message.
Add a custom property to the auth property in the client socketIo options. This will be available to the server event handlers in socket.handshake.auth.xxxx.
Add a custom header to the transportOptions.polling.extraHeaders property in the client socketIo options. This will ONLY be presented when the socket.io client is connected via polling and not when "upgraded" to websockets (as you can't have custom headers then).
Add a custom query property to the client socketIo options. I don't recommend this since it potentially exposes the data to intermediate proxies.

Resources