Emit to socket with a particular object attribute - node.js

I am adding the username to the socket object like this which is working fine
socket.on('add user', function (username) {
socket.username = username;
});
Lets assume the username is khawer and now i want to emit to this socket where username is khawer but i am unable to do so.
I have tried this
io.sockets.connected[socket.username].emit('chat message', msg);
And this
io.sockets.sockets[socket.username].emit('chat message', msg);
But both did not work. What am i doing wrong here?

Just assigning a username property to a socket does not make it so that it's indexed by name - thus you cannot do either of the types of lookups you're doing.
If you want to find a socket by username, you will either have to do a brute force search of all the sockets to find the one that has the same user name or you will have to create your own index of sockets by name.
If you want to do a brute force lookup to find it, you could do this:
var list = io.sockets.sockets;
for (var i = 0; i < list.length; i++) {
if (list[i].username === "khawer") {
list[i].emit('chat message', msg);
}
}
You could also put each user into a chatroom with a name that matches their username. Then, you could send to any given username by simply sending to the chatroom by that name. You'd be using the chatroom feature as an index by username. It would just require one extra step to put a socket into a chatroom that matches their username when they connect.
Or, each time a socket connects and disconnects, you could maintain your own socket index by username (this is relatively common).

Related

is it efficient to store my online socket server users on redis?and if yes then how?

I am building a web socket application using node and i want to store my currently connected users ,so i saved it as a user Id key and socket Id value but when the user disconnect i only knows it's socket Id so i will not be able to remove it from Redis (since Redis only allow search by key) so how would i solve it ? i tried to reverse the key and value but it's impossible as a socket id is not unique
const isUserExist = async (socketId) => {
return await redisClient.get(socketId);
};
const addUser = async (socketId, userId) => {
if (await isUserExist(socketId)) return console.log("user already exist");
await redisClient.set(socketId, userId);
console.log("user is set inside database ");
};```
i have actually discovered that socket object carries the socket id of the current connected user , so i used it and my problem was solved and we also need to store the id as the key and the socket id as the value

socket.io Rooms Connection Limitation

I have question about socket.io.
Is there a way to limit connections in rooms? I want to limit connections room to two users.
So that only the employer and the designer can talk.
To do this, you need some sort of identifier. If you have their username, you can just search for their username in some sort of Map containing username to the respective socket. Then, when you send a message you can use socket.to(anothersocket.id).emit("message", "message content). The other person will message to your socket.id. This is because all sockets are in the room of their id.
use this code and limit the user to join. I think this is the simplest way possible --->>>
io.on("connection", function (socket) {
console.log("User Connected :" + socket.id);
//Triggered when a peer hits the join room button.
socket.on("join", function (roomName) {
let rooms = io.sockets.adapter.rooms;
let room = rooms.get(roomName);
//room == undefined when no such room exists.
if (room == undefined) {
socket.join(roomName);
socket.emit("created");
} else if (room.size == 1) {
//room.size == 1 when one person is inside the room.
socket.join(roomName);
socket.emit("joined");
} else {
//when there are already two people inside the room.
socket.emit("full");
}
console.log(rooms);
});
});

How to get the client id?

I am using the ws lib and I want to make a private chat just like this: client A sends a message to client B.
There I have a ws.clients.forEach() method to broadcast to every client, but how can I get the client id of an individual one?
When you set up your chat system and userA wants to chat with userB, you have to have an identifier that userA users to tell the server they want to chat with userB. If the ws library doesn't provide an easy to use identifier for each connected user, then you need to assign one when the user connects to your server and then keep track of it for each connection in a way you can find the socket that has a given id. One simple way would be to just create a Map object where key is the ID and value is the socket. You can then lookup any socket by id.
For example, you could create a unique ID for each incoming socket connection and store it like this:
const uuid = require("uuid/v4");
const wss = new WebSocket.Server({ port: 8080 });
const idMap = new Map();
wss.on('connection', function connection(ws) {
// create uuid and add to the idMap
ws.uuid = uuid();
idMap.add(ws.uuid, ws);
ws.on('close', function() {
// remove from the map
idMap.delete(ws.uuid);
});
ws.on('message', function(info) {
try {
info = JSON.parse(info);
if (info.action = "send") {
let dest = idMap.get(info.targetId);
if (dest) {
dest.send(JSON.stringify({action: "message", sender: ws.uuid, data: info.message}));
}
}
} catch(e) {
console.log("Error processing incoming message", e);
}
});
});
Then, you could use that uuid value internally to identify which user someone wanted to connect to or send to.
In most real applications, you will also create some sort of login and username. A unique username could also be used as the id.

socket.io emit to specific user and give them x amount of time to respond

I have an online chat. It uses rooms. If a user sends message and the other user is not online, it should increment the "missed messages" counter. I tried to create a timeout with setTimeout and if they emit an event it clears that timeout.
However chatOnline doesn't fire as I expected to, which leads to it always reporting the user is offline and incrementing the counter for missed_texts column in rethinkdb (which is not shown because it isn't relevant).
How can I retrieve if the user is online from socket.io? My goal is to avoid having to store presence info in the database, which could get out of control quickly.
Code I tried:
socket.on('chatSend',function(data){
if(socket.client.user.room_id !== null){
//were storing some crap in the socket object for easy retrieval.
data.user_id = socket.client.user.id;
data.room_id = socket.client.user.room_id;
data.timestamp = ~~(new Date() / 1000);
//insert message into chat table
r.table('chat').insert(data).run().then(function(res){
//retrive generated record from table
r.table('chat').get(res.generated_keys[0]).run().then(function(data2){
io.sockets.in(data2.room_id).emit('chatNew',data);//emit to all users in room
log('chatSend');
//attempt to see if the other user is online
getOtherUser(data2.room_id,socket.client.user.id,function(tid){
log('other user id: %d',tid);
//all users automatically join a room matching their user id when connecting.
//unsure how to see if the user is online. this doesnt work.
//this is what i need help with. retrieving the other users socket resource if they are online,
//and if they are not then return null or false, etc so i can work with that.
var othersocket = io.sockets.in(tid);
//if timeout completes before they respond, they are not online.
var tmptime = setTimeout(function(){
log('other user not online.');
othersocket.removeListener('chatOnline',tmpfunc);
missedTexts(data2.room_id,tid,'INCR');
},5000);
var tmpfunc = function(){
clearTimeout(tmptime);
//remove the listener
othersocket.removeListener('chatOnline',tmpfunc);
};
//emit chatOnline to other user socket
//when they respond, cleartimeout, resulting in counter not being incremented.
othersocket.on('chatOnline',tmpfunc);
othersocket.emit('chatOnline');
});
});
});
}
});

how do I store socket resources from specific users with socket.io?

I'm designing a chat script which I test on my machine using different browsers. I'm tryng to send messages to specific users with socket.io, so here it is :
client:
socket.on('msgFromServer', function (data) {
message = data['message'],
from = data['from'],
to = data['to'];
if($('#chatbox.'+from).dialog("isOpen") === true){
$('#chatbox.'+from+' #messageOutput textarea.readOnly').text(message);
}
else if(($('#chatbox.'+from).dialog("isOpen") !== true)){
createChatbox(from,to,message);
}
});
server:
var users = {};
io.sockets.on('connection', function (socket) {
if( ( users.hasOwnProperty(req.session.name) === false))
users[req.session.name] = socket;
socket.on('msgToServer', function (data) {
for (var u in users){
console.log("%s | %s",u,users[u]);
}
});
});
Well, I'll talk about the structure of code related to the server. It is in charge of storing a user on a 'connection' event. The problem starts when I reload the page: it stores the user from browser A in the users object, if I reload and reconnect stores it again , but when I ask which are the contents of the users object in browser B ... the info is outdated and does not show the same result as when I ask which are the contents of the object in broser A, even though I'm trying to do some cheking of nullity to store vals if users is empty --> if( ( users.hasOwnProperty(req.session.name) === false)). Basically, what I need is a means of storing each socket resource in a container(in fact, doesn't necessarily needs to be an object) with an identifier(req.session.name) and to have such container available to all sessions in all browsers, so when server receives a message from browser A to browser B it could identify it and emit a response to browser B.
I got an I idea of what I wanted from https://github.com/generalhenry/specificUser/blob/master/app.js and http://chrissilich.com/blog/socket-io-0-7-sending-messages-to-individual-clients/
If you look carefully at the code... in chrissilich.com , the author states that we need to store the 'socket.id' (users[incoming.phonenumber] = socket.id), whereas in git generalhenry states we have to store the 'socket'(users[myName] = socket) resource. The latter is the correct one , because the values of socket.id tend to be the same in both browsers... and that value changes automatically , I don't know why is there... I suppose in earlier versions of node it worked that way.
The problem is that socket.id identifies sockets, not users, so if an user has several tabs opened at same time, every tab would have different socket.id, so if you store only one socket.id for an user, every time you assign it, you overwrite previous socketid.
So, beside other possible problems, at least you need to do this or it won't work. I bet that you say about 1 socket for all browsers is that you overwrite the id every time (it happened to me when I started using Socket.IO)
As a general rule, remember that you manage CONNECTIONS and not USERS... an user can have more than one connection!.
On connection
function onConnection( socket ) {
var arr = users[incoming.phonenumber] || null;
if( !arr )
users[incoming.phonenumber] = arr = [];
if( arr.indexOf( socket.id ) === -1 )
arr.push( socket.id ); // Assigns socket id to user
}
On disconnection
function onDisconnect( socket ) {
var arr = users[incoming.phonenumber] || null;
if( !arr ) return; // Should not happen since an user must connect before being disconnected
var index = arr.indexOf( socket.id );
if( index !== -1 )
arr.splice( index, 1 ); // Removes socket id from user
}

Resources