Storing data in Socket.io - node.js

I am now starting in this world of Node.js, I've done several tests, initially as the typical "Hello World", up to the creation of a simple chat, I have embarked on the creation of one a bit more complex, which I describe below:
A chat in which users already exist, which are housed in a MySQL database.
It should create multiple chat rooms, where every conversation is one to one.
Initially I did Mysql connection tests, achieving it efficiently, but coming across problems when closing the connection to it, so I found another "solution" which will ask if it is advisable to do it or the Otherwise it would be a big mistake.
To chat'm making use of Seller Socket.io known, which, reading the documentation I have found related to the ID of the session, you can associate more information, what I'm doing is the following:
I a room through socket.join option (room)
With the property "set" of socket.io, I am storing information related to the connected user, such as name, database id and url of the picture, through an object.
Using io.sockets.clients (room), get users who are in the room, given that a user can have more than one session (several windows / tabs), so do the validation client side with the user id, to show just one.
Server:
var io = require("socket.io").listen(8080, {log: false});
io.on("connection", function(socket)
{
socket.on("newUser", function(data)
{
var participants = [];
var room = data.ig;
socket.set('iduser', data.d, function ()
{
socket.join(room);
for (var ver in pas = io.sockets.clients(room))
{
participants.push({id: pas[ver].store.data.iduser[0].id, n: pas[ver].store.data.iduser[0].n, f: pas[ver].store.data.iduser[0].f});
}
io.sockets.in(room).emit("newConnection", {participants: participants});
});
});
socket.on("disconnect", function()
{
var rooms = io.sockets.manager.roomClients[socket.id];
for(var room in rooms)
{
if(room.length > 0)
{
room = room.substr(1);
}
}
var idusuario = 0;
for(var ver in pas = io.sockets.clients(room))
{
if(pas[ver].id == socket.id)
{
idusuario = pas[ver].store.data.iduser[0].id;
break;
}
}
if(idusuario != 0)
{
//El número de veces que tiene una sesión...
var num = 0;
for(var ver in pas = io.sockets.clients(room))
{
if(pas[ver].store.data.iduser[0].id == idusuario)
{
num++;
}
}
if(num <= 1)
{
io.sockets.in(room).emit("userDisconnected", {id: idusuario, sender:"system"});
}
}
});
});
Client
Appreciate their opinions or suggestions on how to deal with this difficulty.
Thanks in advance for their help.

Related

socket io session map

Hello on entering my backend I make a connection to my socket and send the player data + socket id
good, and then on receiving I add him in line
and perform a function that will fetch 2 players who have approximate points to form a match
and so I wanted to find a match send to the socket of the selected players
But I don't know how best to map session
I saw about express.socket-io
or save one of these socket's that connect in some way
for when I find a match
send my match to my selected socket's
io.on('connection', function (socket) {
//ADD PLAYER TO QUEUE
socket.on('addPlayer-Queue', (result) => {
const player = {
id: result.id,
name: result.name,
mmr: result.mmr
}
const player = new Player(player,socketid )
socket.emit('match', matches)
});
class Player {
constructor(player,socketId) {
this.id = player.id
this.socketId = socketId
this.name = player.name
this.mmr = player.mmr
}
}
Here I get my player and create it, but I don't know how to get this player's socket.id and how to map in a session
If I understood you correctly, here's a way to do it.
Everytime a players get added, I push them on an array and then call the function matchPlayersQueue that tries to match players based on their MMR (I haven't completed the code, but a way to do it, is either check the variance of MMRs or check their difference). If you follow this path, keep in mind that everytime a players disconnect that was on the queue array, you should remove the element.
Another way to do this is, set a timer that periodically calls the function matchPlayersQueue.
let playersOnQueue = [];
io.on("connection", function(socket) {
//ADD PLAYER TO QUEUE
socket.on("addPlayer-Queue", result => {
const player = {
id: result.id,
name: result.name,
mmr: result.mmr
};
const player = new Player(player, socketid);
playersOnQueue.push(player);
const matchedPlayers = matchPlayersQueue(playersOnQueue); // matchedPlayers will be an array of their sockets ids.
// Do something with matchedPlayers, empty playersOnQueue if matchedPlayers.length doesn't equal to 0.
});
});
function matchPlayersQueue(arr) {
//We'll sort the array by mmr.
arr.sort(function(firstPlayer, secondPlayer) {
return firstPlayer.getMMR() - secondPlayer.getMMR();
});
if (arr.length >= 3) {
//Trivial way to match 3 people, not checking for MMR.
if (arr.length === 3) {
const socketIDs = arr.map(function(player) {
return player.getSocketID();
});
return socketIDs;
} else {
/*
Here you can implement your own way of selecting players, maybe having a maximum MMR difference between players or comparing the overall variance of MMR.
*/
}
} else {
//If there are fewer than 3 people.
return [];
}
}
class Player {
/*
Beware of this constructor, while this works, The way i'd would do it is each variable to their own attribute.
*/
constructor(player, socketId) {
this.id = player.id;
this.socketId = socketId;
this.name = player.name;
this.mmr = player.mmr;
}
getMMR() {
return this.mmr;
}
getSocketID() {
return this.socketId;
}
}

How/should one remove old websocket connections?

I'm using 'ws' on Node.js and I'm not really sure how old connections are removed (old connections being disconnects or idle ones, this may be two different issues). Do I need to implement this manually for the two types?
For this you need to first maintain a list of live web socket connection objects in your server end. When the user disconnects from web socket by closing the browser or any other means you need to delete the user web socket connection object from the list.
Server side codes. You can refer to members array in the following code for handling this.
io.on("connection",function(client)
{
client.on("join",function(data)
{
members[membercount] = client;
membername[membercount] = data;
membercount++;
for(var i =0 ;i <members.length;i++)
{
members[i].emit("message",membername);
}
});
client.on("messages",function(data)
{
for(var i =0 ;i <members.length;i++)
{
if(members[i].id !== client.id)
{
members[i].emit("message",data);
}
}
});
client.on("disconnect",function(data)
{
for(var i =0;i<members.length;i++)
{
if(members[i].id === client.id )
{
membercount--;
members.splice(i,1);
membername.splice(i,1);
}
}
for(var i =0 ;i <members.length;i++)
{
members[i].emit("message",membername);
}
});
});
Hope this helps !!!!!

room broadcast vs sockets emit - are they the same?

I'm using my own rooms implementation in Socket.io (got my own Room/Player classes), since I need to perform several filters the room. For now, I save all sockets inside "players" property in a Room, and implemented my own "emit" to the room, the loops players and emits to their sockets.
Is it considerably slower to the traditional broadcast.to('room')? or it basically does what I did to my own room implementation? I'm aiming on having thousands of rooms with 2-4 players in each...
Thanks :)
As you can see by looking at the code for the socket.io adapter .broadcast() on GitHub, all socket.io is doing is looping over a list of sockets and sending a packet to each one (see code below).
So, if your code is doing something similar, then performance would likely be something similar.
Where you might notice a feature difference is if you are using a custom adapter such as the redis adapter that is used with clustering, then the logic of broadcasting to users connected to different servers would be handled for you by the built-in adapter, but may be something you'd have to implement yourself if doing your own broadcast.
Here's the socket.io-adapter version of .broadcast():
Adapter.prototype.broadcast = function(packet, opts){
var rooms = opts.rooms || [];
var except = opts.except || [];
var flags = opts.flags || {};
var packetOpts = {
preEncoded: true,
volatile: flags.volatile,
compress: flags.compress
};
var ids = {};
var self = this;
var socket;
packet.nsp = this.nsp.name;
this.encoder.encode(packet, function(encodedPackets) {
if (rooms.length) {
for (var i = 0; i < rooms.length; i++) {
var room = self.rooms[rooms[i]];
if (!room) continue;
var sockets = room.sockets;
for (var id in sockets) {
if (sockets.hasOwnProperty(id)) {
if (ids[id] || ~except.indexOf(id)) continue;
socket = self.nsp.connected[id];
if (socket) {
socket.packet(encodedPackets, packetOpts);
ids[id] = true;
}
}
}
}
} else {
for (var id in self.sids) {
if (self.sids.hasOwnProperty(id)) {
if (~except.indexOf(id)) continue;
socket = self.nsp.connected[id];
if (socket) socket.packet(encodedPackets, packetOpts);
}
}
}
});
};

Resolve Clients and rooms - Migration from socket.io 0.9 to +1.x

I am trying to adapt a piece of code that was coded with socket.io 0.9 that returns a list of clients in a specific room and list of rooms(typical chat-room example)
Users in room
var usersInRoom = io.sockets.clients(room);
List of rooms
socket.on('rooms', function() {
var tmp = io.sockets.manager.rooms;
socket.emit('rooms', tmp);
});
tmp looks like this
{
0: Array[1],
1: /Lobby: Array[1]
}
So I can show the list in the client with this javascript run on the browser.
socket.on('rooms', function(rooms) {
$('#room-list').empty();
debugger;
for(var room in rooms) {
room = room.substring(1, room.length);
if (room != '') {
$('#room-list').append(divEscapedContentElement(room));
}
}
$('#room-list div').click(function() {
chatApp.processCommand('/join ' + $(this).text());
$('#send-message').focus();
});
});
But for version >1.x I just found the clients/rooms changed.
Following some links I found here, I could manage to get a list of rooms by doing this:
socket.on('rooms', function(){
var tmp = socket.rooms;
socket.emit('rooms', tmp);
});
The problem here is that socket.rooms returns
{
0: "RandomString",
1: "Lobby",
length: 2
}
And I just need to pass the 'Lobby' room. I don't know from where the random string come from.
EDIT
Through some debugging I discovered, the randomstring is the socket.id ... Is it normal this behavior? Returning the room and the socket.id together?
Updated
I finally got some results
Users in room
var usersInRoom = getUsersByRoom('/', room);
function getUsersByRoom(nsp, room) {
var users = []
for (var id in io.of(nsp).adapter.rooms[room]) {
users.push(io.of(nsp).adapter.nsp.connected[id]);
};
return users;
};
List of rooms
function getRooms(io){
var allRooms = io.sockets.adapter.rooms;
var allClients = io.engine.clients;
var result = [];
for(var room in allRooms){
// check the value is not a 'client-socket-id' but a room's name
if(!allClients.hasOwnProperty(room)){
result.push(room);
}
}
return result;
}
Better and more straightforward ways to achieve the results?
These are the links I checked:
How to get room's clients list in socket.io 1.0
socket.io get rooms which socket is currently in
It seems there is no better approach, so I will use my own answer. In case any of you has a better solution, I would update the accepted one.
Users in room
var usersInRoom = getUsersByRoom('/', room);
function getUsersByRoom(nsp, room) {
var users = []
for (var id in io.of(nsp).adapter.rooms[room]) {
users.push(io.of(nsp).adapter.nsp.connected[id]);
};
return users;
};
List of rooms
function getRooms(io){
var allRooms = io.sockets.adapter.rooms;
var allClients = io.engine.clients;
var result = [];
for(var room in allRooms){
// check the value is not a 'client-socket-id' but a room's name
if(!allClients.hasOwnProperty(room)){
result.push(room);
}
}
return result;
}

Disconnect All Users of Room when "Host" Disconnects in Socket.IO

Currently I have the originator of a room marked as the "Host".
I need to set it so that if the "Host" clicks a "Close Room" link, it will disconnect all over users from that room id.
How can I grab all users from socket.manager.roomClients or something of the sorts, loop through them all and run some type of socket.leave( room_id ) if the "Host's" room_id matches the key in the socket manager?
Thanks for any insight or help. Let me know if I need to clarify anything.
There doesn't appear to be a mechanism for this in socket.io, but it's not terribly difficult to implement it yourself. You just iterate over each socket in the room and call the disconnect method. This would ideally be a io.sockets level method, but you've got to know the namespace that the socket is using, so I added a prototype at the socket level to boot everyone out of a room. Take a look at the following:
var sio = require('socket.io');
var io = sio.listen(app);
sio.Socket.prototype.disconnectRoom = function (name) {
var nsp = this.namespace.name
, name = (nsp + '/') + name;
var users = this.manager.rooms[name];
for(var i = 0; i < users.length; i++) {
io.sockets.socket(users[i]).disconnect();
}
return this;
};
You can use it as shown below.
socket.on('hostdisconnect', function() {
socket.disconnectRoom('roomName');
});
A word of warning, if you're going to use something like this, it's important to be aware it uses internal data structures which may or may not be around in the next version of socket.io. It's possible for an update from socket.io to break this functionality.
The latest update in socket.io is the leave() method as a companion to join().
Eg;
socket.join('room1');
socket.leave('room1');
May be this help you in your scenario.
Here is how I managed to achieve this in socket.io "version": "2.1.1" :
function disconnectRoom(room: string, namespace = '/') {
io.of(namespace).in(room).clients((err, clients) => {
clients.forEach(clientId => this.io.sockets.connected[clientId].disconnect());
});
}
After much fighting with trying to do it through socket.io I figured out a way to get the room to be killed through Node.JS/Express
app.get('/disconnect', function(req, res){
console.log("Disconnecting Room " + req.session.room_id);
req.session.destroy(function(err){
if (err){
console.log('Error destroying room... ' + err);
} else{
res.redirect('/');
}
});
});

Resources