Socket.IO socket.id changing for same client without reconnect - javascript

I'm making an application where I need to map sockets to some object. I thought I would be safe using the socket.id variable provided by Socket.IO. Unfortunately while debugging I found out that the socket.id changed without the client disconnecting/reconnecting or whatever.
Here's a small piece of code to show what I'm trying to do:
var io = require('socket.io').listen(8080),
var myVariable = new Array();
// Main event loop
io.sockets.on('connection', function (socket) {
socket.on('myEvent', function(data) {
myVariable[socket.id] = data;
}
// 'someOtherEvent' which uses the mapped data.
socket.on('someOtherEvent', function(data) {
// Doesn't always work because socket.id has changed and var is empty
doSomethingWith(myVariable[socket.id]);
}
});
I'm not sure but I don't think this is the desired effect. Why does this happen and how would I work around it?

I was using node-inspector to debug my application and because I was holding the code on some breakpoint it disconnected the client (probably some timeout client side). That's why I got a new socket.id when I continued the code execution.

Looks like 'someOtherEvent' emited before 'myEvent' or socket has been reconnected. I use socket.id as connection identifier on two different projects in production and it works without any problems.

Related

How to create sockets that particular to user and disposable on Socket.Io

I write a Node.Js app and I use Socket.Io as the data transfer system, so requests should be particular to per user. How can I make this?
My actual code;
node:
io.on('connection', (socket) => {
socket.on('loginP', data => {
console.log(data);
})
})
js:
var socket = io('',{forceNew : false});
$("#loginbutton").click(function() {
var sessionInfo = {
name : $("#login input[name='username']").val(),
pass : $("#login input[name='pass']").val()
}
socket.emit("loginP", sessionInfo)
})
It returns one more data for per request and this is a problem for me. Can I make this on Socket.Io or should I use another module, and If I should, which module?
If I understand your question correctly (It's possible I don't), you want to have just one connection from each user's browser to your nodejs program.
On the nodejs side, your io.on('connection'...) event fires with each new incoming user connection, and gives you the socket for that specific connection. So, keep track of your sockets; you'll have one socket per user.
On the browser side, you should build your code to ensure it only calls
var socket = io(path, ...);
once for each path (your path is ''). TheforceNew option is for situations where you have multiple paths from one program.

Is there some way of importing a variable(specifically map) from the server side onto the client side(Socket.io)?

I am trying to assign unique colors to each different client( by using socket.id ). In my map() I have paired (socket.id,randomcolor()), but this variable is on the server side. I've found out that the require() statement doesn't work on client side,
why is that and what is a solution to it? I want to be able to pass map() variable to the client side so that it uses the color assigned to that socket.id and displays the color accordingly.
Or is there some way to know the socket.id on the client side(I don't think it is but not sure), specifically a users computer has to know who sent the message i.e. what socket.id was used to send the message, Is it possible to know that?
Here's my server side:
var express = require('express');
var app = express();
app.use(express.static('public'))
var http = require('http').createServer(app);
var io = require('socket.io')(http);
const map = new Map();
io.on('connection', function(socket) {
console.log('connected by ' + socket.id);
map.set(socket.id, RandomColor())
socket.on('chat', function(data) {
//emitting to all sockets connected
io.emit('chat', data);
console.log(map.entries());
});
socket.on('typing', function(data) {
socket.broadcast.emit('typing', data);
})
});
http.listen(3000, function() {
console.log('listening on port 3000');
});
Here's client side :
// import '../index';
var socket = io.connect('http://localhost:3000')
var message = document.getElementById('Message');
var handle = document.getElementById('Handle');
var btn = document.getElementById('Send');
var output = document.getElementById('Output');
var feedback = document.getElementById('Feedback');
var ids = []
console.log(server);
//emit event
btn.addEventListener('click', function() {
socket.emit('chat', {
message: message.value,
handle: handle.value,
})
})
message.addEventListener('keypress', function() {
socket.emit('typing', handle.value)
})
messageArray = []
//listening for any message received
socket.on('chat', function(data) {
// console.log(data);
feedback.innerHTML = ""
var item = document.createElement('li')
item.innerHTML = "<span style=\"font-family:\"cursive\";\" ;><strong>" + data.handle + ": " + data.message + "</strong></span>";
document.getElementById('Output').appendChild(item)
})
//listening for any typing event listener
socket.on('typing', function(data) {
feedback.innerHTML = "<p><strong>" + data + " is typing a message </strong></p>";
})
PS: Also, I'm new to JS and Socket.io so please suggest some good practices for anything in the code.
First of all, JS has no built-in include/reference property.
So you can't just join another file into another file. But some libraries achieve this with their own written methods etc.
A JS executed on the client-side is not able to access local files. Although you may access an online file load into the document or to an object. So similar functionality can be achieved via 3rd party scripts.
Node.JS follows the CommonJS module system and uses the power of being able to access the local file system.
About the index: So you don't need a Map and Map is pretty similar to a standard object, main difference is might be the order of contents.
But since all you need is a dictionary object. Just create a simple object. Then you can emit the color index whenever you want.
const colorIndex = {}
colorIndex[socketID] = color
Each can set their color on client-side and send it to the server, on each update server has to update every other client about the color.
A client cannot know other clients otherwise wouldn't be secure and it doesn't work like that. It works more like you are calling someone and the server is a middle man that connecting you two.
So, create an object, store socket ids, nicknames, any other info you need. Keep it on serverside, on each message send all of them together with the message.
const users = {}
io.on('connection', function(socket) {
users[socket.id] = {//Add new user
color:RandomColor()
}
socket.on('chat', function(message) {
let u = users[socket.id];//Get user from index
let data = {//Create a message package
user:(u.username)?u.username:"Guest", //name of the user if set
color:u.color,//color of user
message
}
io.emit('chat', data );//Send
});
socket.on('setColor', function(color) {//User can update color
users[socket.id].color = color
});
socket.on('setname', function(name) {//User can update username
users[socket.id].username = name
});
});
So you probably get the idea. There are bunch of ways to achieve.
I don't think you could send that map as an argument, but you can't try creating an array of arrays and emit it to an event like io.emit(colors, array) and once you have it on the client side you can transform back to a map using something like map or reduce
RequireJS is responsible to handle dependencies and ensure that you have everything you need. It is a Javascript library which can work anywhere you use Javascript at, including your server and client-side. The reason it does not work on your client-side (which manifests in the error you see) is that it's not configured on your client-side.
You can read about configurating RequireJS as well.
However, if you set it up properly on your client-side, then there might still be issues, particularly if you try to use on your client-side something which is available on the server. Client-side is a browser, potentially very far from the server. Luckily there is a client API for Socket.IO.
EDIT
Server and client-side can share values in several ways:
WebSockets (a duplex protocol which should be chosen if available in most cases)
Push notifications
AJAX
Page load
Forever frame (that's a hack which should be avoided)

socket.io assigning custom socket.id

I keep track the list of every users connected in the array.
So if there is a new connection, it will check whether the user is already on the list or not, if he was already on the list, then assign their socket.id with the corresponding socket.id on the list, otherwise just add them to the list.
It's for preventing same user counted as 2 user while he attempt to do multi-login.
Object.keys(client).forEach(function (key) {
if (client[key].id == data.id){
is_connected = true;
socket.id = key;
}
});
I have no problem handling the messages/chat that was sent/received by the user who attempt multi-login.
socket.on('chat', function(msg){
var data = {"name": client[socket.id].name, "message": msg};
io.emit('chat', data);
});
The io.emit for the chat message was succesfully sent to the user who attempting multi-login.
The problem I got was whenever the user decide to logout/disconnect from the server.
io.emit('user_leave', client[socket.id].id);
[Multi-Login Case] -> Multi-User and Dual-User are same user attempting Multi-Login
Whenever the Main-User disconnected from the server, the Dual-User received 'user_leave' sent by the server, because io.emit supposed to send it to all sockets.
But not otherwise, while the Sub-User disconnected from the server, the Main-user do not receive 'user_leave' emitted by the server.
*Note: Main-User is login first, then the Dual-User. So the Main-User information was saved directly in the array, while the Sub-User socket.id was assigned with the Main-User socket.id
[Update]
B2 socket.id was assigned with B1 socket.id, the io.emit for chat work perfectly while io.emit for disconnect only emitted to All except Dual-User(B2)
socket.id is used internally by socket.io for its own socket list. You cannot overwrite that or you break some of its ability to maintain its own data structures.
You have two choices:
You can use the existing socket.id value as is (without overwriting it) so you don't break existing behavior. It is already guaranteed to be unique on the server.
You can use a different property name for your own id such as socket.userId and then you won't conflict.
If you need to, you can maintain a map between your own custom id and the socket.io socket.id so you could get to one from the other.
Similar question here: Socket.io custom client ID
generateId prop of io.engine object can be used for to set the custom id.
Using this way, the all socket ids can be created on the server side without any issue.
Actually I wrote an answer for a similar question today.
An example:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.engine.generateId = function (req) {
// generate a new custom id here
return 1
}
io.on('connection', function (socket) {
// listing the default namespace rooms
console.log("rooms: ", io.nsps["/"].adapter.rooms);
})
The console output would be as the following:
rooms: { '/#1': Room { sockets: { '/#1': true }, length: 1 } }
It seems to be it has been handled.
It must be in mind that socket id must be unpredictable and unique value with considering security and the app operations!
Extra: If socket.id is returned as undefined because of your intense processes on your generateId method, async/await combination can be used to overcome this issue on node.js version 7.6.0 and later. handshake method of node_modules/engine.io/lib/server.js file should be changed as following:
former:
Server.prototype.handshake = function (transportName, req) {
var id = this.generateId(req);
...
}
new:
Server.prototype.handshake = async function (transportName, req) {
var id = await this.generateId(req);
...
}

How can I connect a node.js server page to my sockets server as though it were a client?

I've seen many similar questions here, like this one about connecting servers to sockets, however, that doesn't seem to be working.
My goal is to have one of my server pages occasionally emit an "advance" event to all of the connected clients. My sockets server listener (sockets.js) looks like this
io.on('connection', function (socket) {
socket.on('advance', function () {
console.log('advancing clients');
io.emit('advance');
});
});
and my other server page (queue.js) has this code
var socket = require('socket.io-client');
socket.connect('localhost:5000');
socket.on('connect', function () {
socket.emit('advance');
});
I have also tried
var socket = require('socket.io-client');
socket.connect('localhost:5000');
socket.emit('advance');
Both of those were in the other question I linked, however both of those failed. In the case of the first example, I received the error
socket.on is not a function
and on the second
socket.emit is not a function
it would seem that socket.connect worked, however, socket.on and socket.emit both failed. The idea is that queue.js will emit an event to all the clients, so they know to request the next song from the server.
As bonus points, I happen to be using passport.socketio to authenticate my users (though I don't seem to be getting the "ioAuthFail" message that would normally occur when a user attempts to connect and is not authenticated).
for what it's worth, my socket authentication code looks like
io.use(passportSocketIo.authorize({
//cookieParser:
key:'user',
secret:'secret',
store:new MongoStore({mongooseConnection:mongoose.connection}),
success:onAuthorizeSuccess,
fail:function(){console.log('ioAuthFail')}
}));
If there's a better way to advance the clients than using socket.io, i would also accept that, but for now it seems like this is the best way for me, assuming I can get this working.
EDIT
as mentioned through comments, changing my code to
var io = require('socket.io-client');
socket = io.connect('localhost:5000');
socket.on('connect', function () {
socket.emit('advance');
});
socket.emit('advance');
doesn't break it, but it doesn't work, either.

Socket.IO Scoping Issue (Node.JS)

I am working on a node.js project that I am leveraging Socket.IO in, and am having an issue getting my head around a scoping issue. Here is what I am trying to do:
var io = require('socket.io').listen(80);
session_manager = require('./includes/session_manager');
// client joins the socket server
io.sockets.on('connection', function(client) {
client.on('X.Session.join', function(session_id, client) {
session_manager.joinSession(session_id, function(err, session) {
// do whatever
});
});
// BRING IN MORE LISTENERS/EMITTERS?
require('someModuleIBuild');
});
As you can see I am basically setting up the initial connection, joining a session via a managing class (so I know who to emit to for which session) and then I am trying to dynamically bring in some custom stuff that ALSO is going to be emitting and listening via the socket connection.
So how do I reference this current connection from within the confines of my custom modules? All the examples I have seen have all the "on" and "emit" functions in one file, which seems like it could get out of control pretty quickly.
I am possibly over-thinking/over-complicating this (this is my first node.js project, first socket-based project, first mostly-javascript project....etc) but any help would be appreciated.
create your modules like this and you can pass the client into the module
module.exports = function(client) {
client.on("whatever", function () {
});
client.on("whenever", function (data) {
});
};
and then do the require like this
require('someModuleIBuild')(client);

Categories