Nodejs socket.io socket property becomes null - node.js

So I am trying to retrieve all the sockets connected to a room. However what i noticed was that adding properties to the sockets such as socket.username become undefined when accessed later on in my server script. Therefore I get an error like so TypeError: Cannot read property 'username' of undefined. I am using version 1.3.3 of the module.
here is the code that triggers the error
function clientsInRoom(room){
var clients = [];
console.log(io.sockets.adapter.rooms[room].length); // This prints out the correct number
for(var cliSocket in io.sockets.adapter.rooms[room] ){
clients.push(io.sockets.connected[cliSocket].username); // Causes exception
}
return clients;
}
io.on('connection', function(socket){
socket.on('adduser', function(username,room){
// store the room name in the socket session for this client
socket.room = room;
// store the username in the socket session for this client
socket.username = username;
// Check if they already exist in room
if(clientExists(username,room) == false){
//Tell user they have joined new room
socket.emit('updatechat','you have connected to '+ room);
//echo to room that a person has connected to their room
socket.broadcast.to(socket.room).emit('updatechat', username + ' has joined this room','con');
}
// send client to the room
socket.join(socket.room);
//Update online lists client side
io.sockets.in(socket.room).emit('updatePeople', clientsInRoom(socket.room));
});
});
If anyone could shed some light on this issue as it has been driving me insane, would be greatly appreciatd.

You're iterating over keys of io.sockets.adapter.rooms. Then using those keys to access io.sockets.connected. What you get out of io.sockets.connected is undefined, not your saved username.
Check this iteration code, this is where the problem is.

There were discrepancies between my node modules that caused this problem. Manually copying the modules from an older release resolved the issue

Related

Websockets & NodeJS - Changing Browser Tabs & Sessions

I've started writing a node.js websocket solution using socket.io.
The browsers connects to the node server successfully and I get see the socket.id and all config associated with console.log(socket). I also pass a userid back with the initial connection and can see this on the server side to.
Question: I'm not sure the best way to associate a user with a connection. I can see the socket.id changes every page change and when a tab is opened up. How can I track a user and send 'a message' to all required sockets. (Could be one page or could be 3 tabs etc).
I tried to have a look at 'express-socket.io-session' but I'm unsure how to code for it and this situation.
Question: I have 'io' and 'app' variables below. Is it possible to use the 2 together? app.use(io);
Essentially I want to be able to track users (I guess by session - but unsure of how to handle different socket id's for tabs etc) and know how to reply to user or one or more sockets.
thankyou
The best way to handle the situation is rely on SocketIO's rooms. Name the room after the user's unique ID. This will support multiple connections out of the box. Then, whenever you need to communicate with a particular user, simply call the message function and pass in their id, the event, and any relevant data. You don't need to worry about explicitly leaving a room, SocketIO does that for you whenever their session times out or they close their browser tab. (We do explicitly leave a room whenever they log out though obviously)
On the server:
var express = require('express');
var socketio = require('socket.io');
var app = express();
var server = http.createServer(app);
var io = socketio(server);
io.on('connect', function (socket) {
socket.on('userConnected', socket.join); // Client sends userId
socket.on('userDisconnected', socket.leave); // Cliend sends userId
});
// Export this function to be used throughout the server
function message (userId, event, data) {
io.sockets.to(userId).emit(event, data);
}
On the client:
var socket = io('http://localhost:9000'); // Server endpoint
socket.on('connect', connectUser);
socket.on('message', function (data) {
console.log(data);
});
// Call whenever a user logs in or is already authenticated
function connectUser () {
var userId = ... // Retrieve userId somehow
if (!userId) return;
socket.emit('userConnected', userId);
}
// Call whenever a user disconnects
function disconnectUser () {
var userId = ... // Retrieve userId somehow
if (!userId) return;
socket.emit('userDisconnected', userId);
}

Reconnect socket in disconnect event

I am trying to reconnecct the socket after the disconnect event is fired with same socket.id here is my socket config
var http = require('http').Server(app);
var io = require('socket.io')(http);
var connect_clients = [] //here would be the list of socket.id of connected users
http.listen(3000, function () {
console.log('listening on *:3000');
});
So on disconnect event i want to reconnect the disconnected user with same socket.id if possible
socket.on('disconnect',function(){
var disconnect_id = socket.id; //i want reconnect the users here
});
By default, Socket.IO does not have a server-side logic for reconnecting. Which means each time a client wants to connect, a new socket object is created, thus it has a new id. It's up to you to implement reconnection.
In order to do so, you will need a way to store something for this user. If you have any kind of authentication (passport for example) - using socket.request you will have the initial HTTP request fired before the upgrade happened. So from there, you can have all kind of cookies and data already stored.
If you don't want to store anything in cookies, the easiest thing to do is send back to client specific information about himself, on connect. Then, when user tries to reconnect, send this information again. Something like:
var client2socket = {};
io.on('connect', function(socket) {
var uid = Math.random(); // some really unique id :)
client2socket[uid] = socket;
socket.on('authenticate', function(userID) {
delete client2socket[uid]; // remove the "new" socket
client2socket[userID] = socket; // replace "old" socket
});
});
Keep in mind this is just a sample and you need to implement something a little bit better :) Maybe send the information as a request param, or store it another way - whatever works for you.

Socket.io disconnect client by id

I'm new to nodejs and trying to write a chat room as so many people have.
The chat consists of multiple rooms and clients. Commands such as /nick /join /help /ls users /ls rooms work as you would expect although I'm having trouble with getting a /kick command to work.
I'm just not sure how you disconnect a client by id, so far /kick client is able to present the respective clients socket.id although I'm stuck for the code to kick via socket.id.
Code so far:
Disconnect client who sent /kick: socket.disconnect();
Delete client from arg /kick client: delete io.sockets.sockets[client];
Deleting the client doesn't disconnect them though, they can still receive data just not send it.
Solved
CuriousGuy's 0.9 worked flawlessly, for those interested - here is the code I'm using.
Server side:
handleClientKick(socket);
...
function handleClientKick(socket) {
socket.on('kick', function(client) {
if (typeof io.sockets.sockets[client] != 'undefined') {
socket.emit('message', {text: nickNames[socket.id] + ' kicked: ' + nickNames[client]});
io.sockets.sockets[client].disconnect();
} else {
socket.emit('message', {text: 'User: ' + name + ' does not exist.'});
}
});
}
Client side:
kickClient = function(client) {
this.socket.emit('kick', client);
};
The following code works with Socket.IO 1.0, however I'm not sure that this is the best solution:
if (io.sockets.connected[socket.id]) {
io.sockets.connected[socket.id].disconnect();
}
Update:
With Socket.IO 0.9 the code would be slightly different:
if (io.sockets.sockets[socket.id]) {
io.sockets.sockets[socket.id].disconnect();
}
This is an old question but if anyone wonders for newer versions;
In Socket.IO v4.X io.sockets.connected[socket.id] or io.sockets.sockets[socket.id] is not working.
So we need to do like this;
io.sockets.sockets.forEach((socket) => {
// If given socket id is exist in list of all sockets, kill it
if(socket.id === givenSocketID)
socket.disconnect(true);
});
Alternate solution In Socket.IO v4.X
For all sockets
const sockets = await io.fetchSockets();
For particular socket
const sockets = await io.in(theSocketId).fetchSockets();
Iterate sockets
for (const socket of sockets) {
console.log("socket id",socket.id);
socket.disconnect(true);
}
Reference link
Here's another option for Socket.IO v4 that doesn't require async syntax:
io.sockets.sockets.get(socket.id)
Someone can correct this if it's wrong, but I think each socket has a unique ID, so there should be no need for iterating.

socket.io Detect what room a user has disconnected from

I have found a few solutions but they seem dates and are not working.
How can I detect what room a user was disconnected from?
I don't know what do you mean about room , but if it is a chat app or something like that
you can create a variable on socket object like roomID when first connection.
io.sockets.on("connection",function(socket){
socket.on("new:user",function(userDATA){
// create variable as roomID on socket.
socket.roomID = userDATA.roomID
});
// other events
socket.on("disconnect",function(data){
var roomID = socket.roomID;
// do what you want to do
});
});
With socket.io 1.0 you can do this with following code:
socket.on('disconnect', function() {
console.log(socket.rooms);
});
It will output an array of rooms the user was joined to.
Note that each socket automatically joins a room named with this socket's ID. So you might see something like
[ 'hIP6r4z8Ym1n5SQUAAAA', 'my_room_name' ]
in your console.

Destroying a handshake after logout. socket.io

Hello I am trying to build chat into an application. What I am wondering is when the user logs out of the website how do I also destroy the socket.io handshake associated with that session so the user cannot send messages from say another tab when he is logged out.
I am using expressjs if that is any help.
Well in case anyone ever find this and wants to know I did figure it out.
You can access the sockets disconnect function. I had object of users ids and their socket id so when someone logged out I called
app.get("/logout", function(req,res){
//do other logging out stuff
sockets.disconnectUser(req.session.user_id);
}
// Disconnect User function
sockets.disconnectUser = function(user_id){
sockets.socket(users[user_id]).disconnect();
}
The socket.io object contains information about all connected sockets and the sessionID of each socket. Thus, it is possible to iterate through the connected sockets and disconnect those which are associated with the sessionID that is logging out. There is no need to manually track user and socket ids in this approach.
Example code tested with socket.io#2.2.0, express#4.17.1 and express-session#1.16.2.
const SocketIO = require('socket.io');
let sio = new SocketIO;
app.get('/logout', function(req, res) {
//do other logging out stuff
logoutSocketsIO(req.sessionID);
});
// Iterate through all connected sockets and close those which are associated
// with the given sessionID
// Note: One sessionID can have multiple sockets (e.g. many browser tabs)
function logoutSocketsIO(sessionID) {
let connections = sio.sockets.connected;
for(let c in connections) {
let socketSessionID = connections[c].conn.request.sessionID;
if(sessionID === socketSessionID) {
connections[c].disconnect();
}
}
}

Resources