Node js chatting with multiple users in same room - node.js

I am new to node.js. I am creating a chat app. I want to manage 4 users who are connected to the room. I have the primary key of the logged in user User A in the session, I can also save it in hidden file. When the page loads I only have one socket connected to server (i.e. socket of User A). When user A wants to chat with User B, I have a textarea for message, a div to show the chat message history and one send button.
How can I allow user A to chat with N number of users and keep track of who's chatting who?
I am thinking of following method to keep track of the chat messages,
Save both users primary key in hidden fields. When user A send message
to User B. Send User A and B's PK to server along with message and
save it in database.
or...
Is there any way to identify User B's Socket. So that Saving/Handling
User A and B's primary on client side can be avoided.

You can broadcast the message to all connected users.
io.sockets.emit("method","Message");
or you can send message to the specific user socket
io.sockets.socket(socketId).emit("method","Message");
Also you can add the users to the specific room on connection
io.sockets.on('connection', function (socket) {
socket.join("RoomName");
});
and send message to the room ( Message will deliver to all joined users )
io.to('RoomName').emit("method","Message");
You can get the socket id of user on connection and save it to array or database .
io.sockets.on('connection', function (socket) {
console.log( "User connected on " + socket.id);
});
Chat Application :
Node.js is single thread application.So You will have all connected user socket object stored in the io.sockets
Step 1: Save socket id of user in to database on Connection
Step 2: Remove Socket id of user from database on connection
Step 3: if socket id of user exist in database then this mean user is connected and you can send the message to user socket.
Node.js
// User Socket array storage
var user_by_socket = [];
var socket_to_users = [];
io.sockets.on('connection', function (socket) {
// Save Socket Object
user_by_socket[socket.id] = socket;
socket_to_user["Jhon"] = socket;
user_by_socket[socket.id] = socket;
socket_by_user["David"] = socket;
//Send Message
socket.on('send',function(data){
// Sent by username
socket_by_user["David"].emit("recieve","{ "From" : 'Jhon' , 'message' : "Hello" }");
//Sent by Socket Id
user_by_socket["Socket ID"].emit("recieve","{ "From" : 'Jhon' , 'message' : "Hello" }");
});
});

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

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.

how does a user connected to a room using socket.io get the data that was send to the room before he joined it

a user joins a room and emits its username to the room using passport.socketio . With the help of socket.request.user.user_id i am able to get the user_id of the socket and fetch the username corresponding to user_id from the database.
function onNewplayer_GK (data) {
var room_id = find_Roomid_GK();
var room = room_List_GK[room_id];
//join the room
this.room_id = room_id;
this.join(this.room_id);
console.log("created new room with id " + room_id);
console.log("created new player with id " + this.id);
var user_id = this.request.user.user_id;
db.collection('user-details').findOne({_id:ObjectId(user_id)},(err,result)=>{
var results = JSON.stringify(result);
var results = JSON.parse(results);
console.log('name is'+ (results.user_name));
var name = results.user_name;
this.emit('myname',{
my_name:name
});
this.broadcast.to(room_id).emit('enemyname',{
enemy_name:name
});
});
io.on('connection',(socket)=>{
console.log('user connected ' + socket.id);
// listen for new player
// socket.on("new_player", onNewplayer);
socket.on('new_player_history',onNewplayer_history);
socket.on('new_player_GK',onNewplayer_GK);
find_Roomid_GK() is a function which looks for rooms which are not filled and if filled it will create a room and assigns it to room_id.There is no problem in the functionality of the rooms. I just want to emit both the user's usernames to each other. how?? I am unable to emit the first user connected username to the second user connected after the first.
But when another user connects and joins the same room, it is able to emit its details to the connected sockets. But this socket is not able to get the details of the sockets connected before this socket which is reasonable enough to understand.
Does anyone have an idea on how this newly connected socket will fetch the username of the connected sockets using passport.socketio ?

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

Emit to socket with a particular object attribute

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

Resources