socket.io assigning custom socket.id - javascript

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);
...
}

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 response: How to get server to respond to multiple clients upon receiving info from all clients?

I'm learning about node.js, express, and socket.io
I've managed to successfully set up my server and connect to it from the browser using localhost:3000
I can send information from client to server and vice versa no problems.
What I now need to do is this.
Let's say we have 2 clients connected to the server.
Each client will send a piece of information.
socket.emit('mydata', datavariable);
On server:
socket.on('mydata', function(datavariable) {
myArray.push(datavariable); //myArray declared globally
});
How do I code it so the server will do something like this.
if(myArray.length = #ofconnectedclients) {
socket.emit("filledUpArray", myArray);
}
Or another way of saying it I guess is, once you have gotten a response from all clients connected, then do something.
I want to receive a piece of information from all clients, and after I have received that info from all clients, then send that data (now stored in an array) to all my clients.
Thanks!
So, it sounds like your problem statement is this:
When all connected clients have sent a certain piece of information to the server, then I want the server to do something.
First thing you have to do is keep track of all connected clients. You can either use socket.io's internal data structures for that or you can create your own. Here's creating your own on the server:
// map of all currently connected client objects
// socket is the key, custom object is the value
// methods are for managing data in the Map
let connectedSockets = new SocketMap();
// socket connects
io.on('connection', function(socket) {
let socketData = {};
socketData.socket = socket;
socketData.dataReceived = false;
// add it to our Map
connectedSockets.set(socket, socketData);
// monitor disconnects too
socket.on('disconnect', function() {
// remove it from the map
connectedSockets.delete(socket);
});
});
The second thing you need to do is to keep track of some per-socket state so you can initailize it at some point and then each time you receive a specific message from a client, you can check to see if all clients have sent the desired message. We'll create a couple functions to do that:
// this actually physically goes above the previous code, but I'm just placing it
// here for explanation purposes
class SocketMap extends Map {
// clear the dataReceived Boolean to false for all sockets
initReceived() {
for (let [key, value] of this) {
value.dataReceived = false;
}
}
// return Boolean if all sockets have dataReceived set to true
haveAllReceived() {
for (let [key, value] of this) {
if (!value.dataReceived) {
return false;
}
}
return true;
}
setReceived(socket) {
let data = this.get(socket);
if (!data) {
return false;
}
data.dataReceived = true;
}
}
Now you have methods on the connectedSockets Map object for keeping track of a dataReceived Boolean for each socket and for initializing them all to false and for setting one to true when a particular condition occurs (receiving a message) and for checking if they have all set the flag. So, you just have to determine when, in your code you want to call those messages.
Let's suppose that when the message you're waiting for from each socket is someMsg. So, you create a listener for that message and process it. We can incorporate that code into our previous connection handling code here:
// map of all currently connected client objects
// socket is the key, custom object is the value
// methods are for managing data in the Map
let connectedSockets = new SocketMap();
// socket connects
io.on('connection', function(socket) {
let socketData = {};
socketData.socket = socket;
socketData.dataReceived = false;
// add it to our Map
connectedSockets.set(socket, socketData);
// monitor disconnects too
socket.on('disconnect', function() {
// remove it from the map
connectedSockets.delete(socket);
});
// process this message
socket.on('someMsg', function(data) {
connectedSockets.setReceived(socket);
// if all sockets have now sent their data, then do something
if (connectedSockets.haveAllReceived()) {
// do your thing here
// reset state, ready for next time
connectedSockets.initReceived();
}
});
});

socket.io - How to join a room

I am writing a chatting app. Right now, when a new conversation between user A and user b is initiated by user A, user A sendS a socket message to server with user B's userId.
The Server checks whether there's a conversation existing between the two users, if not, creates one, and have user A join the new conversation(clientA.join(newConversationId)). But I don't know how to have user B join the room too if user B actually has a connected socket now.
What I think might work is keeping an object mapping userId to socket.id, so I can get user B's socket id by B's userId sent along with A's origin message. And then I'll get B's socket by its socket ID, and have it join the conversation too.
The problem is, I don't know how to get a socket by a socket ID. I don't think there's an official document of this. Or is there other better way to deal with something like this?
Yes, you have to keep track of your users id.
This code may help you a little with that.
var io = require("socket.io").listen(conf.port);
var customIds = [];
io.on("connection", function (socket) {
socket.on("login" function (data) {
customIds[socket.id] = data.userId;
});
/**
* Executes when a client disconnect.
* It deletes this client and updates and emits the client new client list
*/
socket.on("disconnect", function () {
// leave the current room
//socket.leave(socket.room);
// emit event
//socket.broadcast.to(socket.room).emit("clientDisconnected",customIds[socket.id]));
// delete the custom id from the custom id array.
customIds.splice(socket.id, 1);
});
}
You can also save your userid like this (Do not modify socket.id)
socket.userId=XXXX
Get a list of clients and look for the user id you need
io.sockets.clients();

how can you tell which socket connection clicked a button using socket.io?

if you have a a button on the page and you want to make sure the button cannot be clicked again before another socket clicks their button.
if socket a clicked i should disable the button until another socket sends a message that they clicked their button and back and fourth.
$(".buttonTurns").on("click", function(){
socket.emit("turnbutton click")
})
and they dont input any names when they connect
Every socket.io session is automatically assigned a unique string as an id. On the server you can get it from:
socket.id
If you are inside a session (that is, the connection that returns the socket object) you can send messages back to the client by simply doing:
socket.emit('event name',data);
But if you want to send a message to a different session then you need to do:
io.sockets.socket(socket_id).emit('event name',data);
If you want to send a message to all connected sessions then just do:
io.emit('event name', data); // broadcast
If your application have state that needs to be managed, then one technique is to store them in an object using the socket id as the key:
var buttonState = {}
io.on('connection',function('socket'){
// default button to off when socket connects:
bottonState[socket.id] = 'off';
socket.on('on button',function(){
bottonState[socket.id] = 'on';
})
socket.on('off button',function(){
bottonState[socket.id] = 'off';
})
})
Now that you can manage the individual state for each individual client on the server you can use that to communicate them to other clients.
You can set a UUID for each client (either client side javascript or server side javascript).
For a server side solution:
As #slebetman mentioned in the comments, socket.io has a built-in unique identifier for each socket.
as #slebetman suggested in his answer, to get the socket's unique ID () see his answer for more details):
// v0.6.x
var sid = socket.sessionId;
// v0.7.x
var sid = socket.id;
For a client side solution:
If you set the unique ID on the client's side, than you probably need to create your own unique ID.
I took the following code from 6 revs and Briguy37's answer to a question here on this site:
function generateUUID(){
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
return uuid;
};
The randomness should be strong enough for all your clients to have a unique identifier using this ID generator. This should work with the code you wanted to use regarding your question here.

Categories