Get the list of connected users/client in Socket.io - node.js

I need to get a list of connected users or clients from socket.io for real time chat.
List of connected user image
I managed to display a list of client who connect to my route/API (localhost:3003/chat). Who ever access this route (authorize or not) will be displayed as shown in the picture. My problem is on initial access or if you try to refresh the browser the client will not see the currently connected users or history of client connections.
This is my sample code for socket.io for server side,
module.exports.initializeSocketIO = (io) => {
io.on('connection', (socket) => {
socket.on('connectedUser', (users) =>{
socket.name = users;
io.emit('connectedUser', users);
console.log(users + ' has joined the chat.');
});
socket.on('disconnect', (user) => {
io.emit('disconnect', user);
console.log(socket.name + ' has left the chat.');
});
socket.on('chatMessage', (from, msg) => {
io.emit('chatMessage', from, msg);
console.log('Message From: ' + from + '\n -' + msg);
});
socket.on('showTypingUser', (user) => {
io.emit('showTypingUser', user);
});
});
};
This is my sample code for socket.io for client side
var socket = io();
socket.on('connectedUser', function(users){
var name = $('#currentUser').val();
var me = $('#user').val(name);
socket.name = users;
if(users != me){
$('#connectedUser').append('<tr><td>' + '<b>' + users + '</b>' + ' has
joined the discussion. </td></tr>' );
}
});
socket.on('chatMessage', function(from, msg){
var me = $('#user').val();
var color = (from == me) ? 'green' : '#009afd';
var from = (from == me) ? 'Me' : from;
var date = new Date();
if(from == "Me"){
$('#messages').append('<div class="speech-bubble">' + '<b style="color:' + color + '">' + from + ':</b> ' + msg + ' <span class="pull-right" style="color:gray">' + date.toLocaleString() + '</span>' +'</div>');
} else {
$('#messages').append('<div class="speech-bubble-reply">' + '<b
style="color:' + color + '">' + from + ':</b> ' + msg + ' <span class="pull-right" style="color:gray">' + date.toLocaleString() + '</span>' +'</div>');
}
});
socket.on('showTypingUser', function(user){
var name = $('#currentUser').val();
var me = $('#user').val(name);
if(user != me) {
$('#notifyUser').text(user + ' is typing ...');
}
setTimeout(function(){ $('#notifyUser').text(''); }, 10000);;
});
socket.on('disconnect', function(user){
var name = $('#currentUser').val();
var me = $('#user').val(name);
if(socket.name != name){
$('#connectedUser').append('<tr><td>' + '<b>' + socket.name + '</b>' + ' has left the discussion. </td></tr>' );
}
});
$(document).ready(function(){
var name = $('#currentUser').val();
$('#welcomeMessage').append('<div class="welcome-chat">Welcome to Entrenami Chat</div>')
socket.emit('connectedUser', name);
});
NOTE: I'm using express for my route and controller and EJS for my view. I'm a bit stuck here because I don't know where to look on how to solve my problem.

io.engine.clientsCount return the total count of connected users you can send it to client when a user get connected like this:
io.on('connection', (socket) => {
var total=io.engine.clientsCount;
socket.emit('getCount',total)
});

Related

How do I pass socket.io room name from one client to another?

I'm using socket.io v4 with NodeJS (Express).
So, the situation that I have is that I have a chat window for client and the client can enter their email and message. Now, I want to create a room based on that email and have another client join that room. My current implementation doesn't work. It creates the room but the other client is unable to join it.
Server Side Code:
io.of(/^\/dynamic-[a-zA-Z0-9]+$/).on("connection", (socket) => {
let email;
const namespace = socket.nsp.name;
let namespaceToCheck = namespace.split('-');
//console.log(namespaceToCheck[1])
User.findOne({apiKey: namespaceToCheck[1]})
.then((doc)=> {
if(namespaceToCheck[1] == doc.apiKey) {
socket.once("pass-email", (data) => {
io.of(namespace).emit("pass-email", data);
email = data;
})
console.log("Valid Connection");
socket.on("chat-message", (msg) => {
socket.join(email, function(){
//console.log(`Socket now in ${socket.rooms}`);
});
//console.log(msg);
console.log(socket.rooms);
Message.findOne({namespace: namespace})
.then((doc) => {
// console.log(doc);
doc.messages.push(msg);
doc.save().then((saved) => { return Promise.resolve(saved) });
})
// console.log(socket.handshake);
//io.of(namespace).sockets.in(data).emit("chat-message", msg);
console.log(email);
io.of(namespace).to(email).emit("chat-message", msg);
})
}
})
.catch((err)=> {
console.log(err);
})
});
Client Socket Code (from which I'm passing the email)
var chatSocket = io("http://localhost:3000/dynamic-8171d2a713d65c5edf81e45af4d14558a2c62275df05c73ca198a94d422e5948");
var chatBtn = document.querySelector('.chat-btn');
var input = document.querySelector('.chat-input');
var messages = document.querySelector(".messages");
var emailInputTag = document.querySelector(".email-input");
input.addEventListener("keypress", function(event) {
if (event.key === "Enter") {
event.preventDefault();
if (input.value && emailInputTag.value) {
chatSocket.emit('create-room', emailInputTag.value);
//chatSocket.join(emailInputTag.value)
chatSocket.emit('chat-message', emailInputTag.value + ':' + input.value);
input.value = '';
}
}
});
chatBtn.addEventListener('click', function(e) {
e.preventDefault();
if (input.value && emailInputTag.value) {
chatSocket.emit('chat-message', emailInputTag.value + ':' + input.value);
input.value = '';
}
});
chatSocket.on('chat-message', function(msg) {
var item = document.createElement('div');
item.classList.add('msg');
item.textContent = msg;
messages.appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});
This is the rooms that this socket is in:
Client chat socket code (from which I want to receive room event and join the received room)
var chatSocket = io(`http://localhost:3000/dynamic-${ApiKey}`);
chatSocket.on("connect", function () {
console.log("Connected-test-succeeded");
// Connected, let's sign-up for to receive messages for this room
});
chatSocket.on("pass-email", (val) => {
console.log(val);
console.log("Listening");
});
var chatBtn = document.querySelector(".chat-btn");
var input = document.querySelector(".chat-input");
var messages = document.querySelector(".messages");
input.addEventListener("keypress", function (event) {
if (event.key === "Enter") {
event.preventDefault();
if (input.value) {
chatSocket.emit();
chatSocket.emit(
"chat-message",
"Owner" + ":" + input.value
);
input.value = "";
}
}
});
chatBtn.addEventListener("click", function (e) {
e.preventDefault();
if (input.value) {
chatSocket.emit(
"chat-message",
"Owner" + ":" + input.value
);
input.value = "";
}
});
chatSocket.on("chat-message", function (msg) {
console.log("Message received: " + msg);
var item = document.createElement("div");
item.classList.add("msg");
item.textContent = msg;
messages.appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});
P.S: I know that the code is a bit messy but what I'm trying to do here is making a tawk.to clone so different namespace for each website url and the rooms are different in the said namespace for different users.

Socket io connected at same time with same socket.id

I developing chat app using socket io. The problem is when two socket connected at same time will produce same socket.id. So when socket disconnect, it will remove both socket. so the other one cannot listening anymore. How i can prevent socket connected with same id? Thank You.
app.post("/online", bodyP, async (req, res) => {
var userID = req.body.username;
var connectStatus;
try {
if (onlineUsersArray.indexOf(userID) === -1) {
connectStatus = true;
var io_online = io.of("/online");
io_online.on("connection", async function (socket) {
var date = new Date().toLocaleString("en-US", { timeZone: "Asia/Singapore" });
onlineUsersArray.push(userID.toString());
users[socket.id] = userID;
console.log('sessionID ' + socket.id);
console.log(users[socket.id] + " connected at " + date);
console.log("User in array : " + onlineUsersArray);
console.log("Number of users connected : " + onlineUsersArray.length);
socket.on("disconnect", function () {
var date = new Date().toLocaleString("en-US", { timeZone: "Asia/Singapore" });
if (onlineUsersArray.indexOf(users[socket.id]) === -1) {
console.log(">>>>>>>>>>>>>>>> " + users[socket.id] + "Not existed in online array");
} else {
var indexOfConnection = onlineUsersArray.indexOf(users[socket.id]);
onlineUsersArray.splice(indexOfConnection, 1);
console.log("user " + users[socket.id] + " disconnected at " + date);
console.log("Number of users connected : " + onlineUsersArray.length);
console.log("Users Currently Online: " + onlineUsersArray);
}
socket.removeAllListeners();
io_online.removeAllListeners();
});
});
} else {
console.log("user " + userID + " already in session");
connectStatus = false;
}
res.status(200).send({
status: true,
connection: connectStatus
});
}
catch (e) {
res.status(200).send({
status: false,
result: "Failed to set online user"
});
logg.loggerServer("app.js - Online user error ", "0", "Failed in online " + e);
}
});
if 2 or more device run at same time it will produce same socket id

NodeJS - socket.broadcast.to not emitting inside nested listeners

I'm trying to emit messsage to specific rooms, once the "joinedRoom' listener is triggered by the client, the code works fine if I place my code outside the joinedRoom listeners, otherwise it does nothing.
Code:
app.get('/room/:room/user/:user', function(req, res){
var room = {
username: req.params.user,
roomname: req.params.room
};
res.render('room', room);
});
var users = {};
io.sockets.on('connection', function (socket) {
socket.on('joinedRoom', function(roomData){
socket.username = roomData.username;
socket.room = roomData.roomname;
console.log("Roomname: " + socket.room);
console.log("Username: " + socket.username);
socket.join(socket.room);
socket.broadcast.to(socket.room).emit('newUser', socket.username);
socket.on('disconnect', function(){
socket.broadcast.emit('userLeft', socket.username);
socket.leave(socket.room);
console.log('Connection id: ' + socket.id);
});
});
});
I saw the docs and some sample code it everything seeems to be correct (when it comes simply to syntax) am I missing something simple here?
Thanks!
EDIT
Client code:
var socket, roomname, ioRoom;
var socket = io.connect('http://localhost:3000');
socket.on('enterRoom', function(roomname){
console.log("ENTERED ROOM: " + roomname);
});
socket.on('newUser', function(username){
pushUserName(username);
pushUserStatus(username, ' has joined the room <br/>')
});
socket.on('newRoom', function(data){
alert(data)
});
socket.on('userLeft', function(username){
pushUserStatus(username, ' has left the room <br/>')
})
function pushUserName(username){
var el = document.getElementById("username");
el.innerHTML += username + '<br/>';
}
function pushUserStatus(username, message){
var el = document.getElementById("joined");
el.innerHTML += username + message;
}

Add to my chat nodejs express another my function

I have a basic chat working with NodeJS, express, and socket.io. I'm trying to add my own simple javascript function to the index.html. When I send message to other connected users, my function LoadRect() is draw a square but only on my web page. I want THE SAME square to be displayed on the other users screens with message. The message has random coordinates x, y, and random color (massege, x, y, c). Help me please. (Sorry for my english.)
index.html
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script>
var x;
var y;
var c;
function LoadRect() {
var e = document.createElement("div");
e.className = "rect";
document.body.appendChild(e);
e.appendChild(document.createTextNode("Square"));
e = e.style;
e.width = e.height='100px';
e.position = "absolute";
x = Math.floor(Math.random()*400+200)+'px';
y = Math.floor(Math.random()*400)+'px';
c = '#'+(Math.random()*0xFFFFFF<<0).toString(16);
e.left = x;
e.top = y;
e.background = c;
}
var socket = io.connect('http://localhost:8181');
socket.on('connect', function(){
socket.emit('adduser', prompt("What's your name?", "Guest"));
});
socket.on('updatechat', function (username, data) {
$('#conversation').append('<b>' + username + ':</b> '+ data + '</br>');
});
socket.on('updateusers', function(data) {
$('#users').empty();
$.each(data, function(key, value) {
$('#users').append('<div>' + key + '</div>');
});
});
$(function(){
$('#datasend').click( function() {
var message = $('#data').val();
$('#data').val('');
//LoadRect();
LoadRect();
socket.emit('sendchat', message, x, y, c);
});
$('#data').keypress(function(e) {
if(e.which == 13) {
$(this).blur();
$('#datasend').focus().click();
}
});
});
</script>
<b>USERS</b>
<div id="users"></div>
<div id="conversation"></div>
<input id="data" />
<input type="button" id="datasend" value="send" />
app.js
var express = require('express')
, app = express()
, http = require('http')
, server = http.createServer(app)
, io = require('socket.io').listen(server);
server.listen(8181);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
var usernames = {};
io.sockets.on('connection', function (socket) {
socket.on('sendchat', function (data, x, y, c) {
io.sockets.emit('updatechat', socket.username, data + ' [x=' + x + '; y=' + y + '; c=' + c +']');
});
socket.on('adduser', function(username){
socket.username = username;
usernames[username] = username;
socket.emit('updatechat', 'SERVER', ' connected');
socket.broadcast.emit('updatechat', 'SERVER', username + ', hello');
io.sockets.emit('updateusers', usernames);
});
socket.on('disconnect', function(){
delete usernames[socket.username];
io.sockets.emit('updateusers', usernames);
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ', bye');
});
});

interprocess communication and clustering for a chat with redis and socket.io

I'm creating an advanced chat system with nodejs, socket.io and redis.
The goal is to have a chat system where one joint chat room is for all users but with an extra possibility to get a private chat with a stuff for one-to-one support.
my app.js looks like the following:
/********************************** Required **********************************/
var express = require('express')
, app = express()
, http = require('http')
, server = http.createServer(app)
, io = require('socket.io').listen(server);
var cluster = require('cluster')
, http = require('http')
, numCPUs = require('os').cpus().length;
/* this is for redis integration */
var redis = require('redis')
, RedisStore = require('socket.io/lib/stores/redis')
, pub = redis.createClient()
, sub = redis.createClient()
, client = redis.createClient();
io.set('store', new RedisStore({
redisPub: pub,
redisSub: sub,
redisClient: client
}));
/********************************** Required **********************************/
/********************************** Objects **********************************/
function User(socketID, username, role) {
var _socketID = socketID;
var _username = username;
var _role = role;
var _userObjectList = null;
console.log('user is being created');
this.getSocketID = function() {
return _socketID;
}
this.getUsername = function() {
return _username;
}
this.getRole = function() {
return _role;
}
this.setRole = function(role) {
_role = role;
_userObjectList = new Array();
}
this.addUser = function(userObject) {
_userObjectList[userObject.getUsername()] = userObject;
}
this.getUserCount = function() {
if(_role != Roles.staff)
return -1;
return Object.keys(_userObjectList).length;
}
}
/********************************** Objects **********************************/
/********************************** Static Variables **********************************/
function Roles() {
}
Roles.user = "user";
Roles.staff = "staff";
/********************************** Static Variables **********************************/
/********************************** Variables **********************************/
// usernames which are currently connected to the chat
var usernames = {};
var staff = {};
var socketIDList = {};
var users = new Array();
/********************************** Variables **********************************/
/********************************** Functions **********************************/
function getUsernames() {
return Object.keys(users);
}
function getStaffObjects() {
var objects = new Array();
console.log('getting users ' + Object.keys(users));
for (key in users) {
console.log('testing if ' + key + ' is a staff member');
if(users[key].getRole() == Roles.staff) {
console.log(key + ' is a staff member');
objects[key]=users[key];
}
}
return objects;
}
function isValidUsername(username) {
if(username == null)
return false;
var validation = /^[a-zA-Z]\w{2,20}/g;
return validation.test(username);
}
/********************************** Functions **********************************/
/********************************** Cluster **********************************/
if (cluster.isMaster) {
server.listen(8080);
console.log('listening now');
// Fork workers.
console.log('i am the master');
for (var i = 0; i < numCPUs; i++) {
console.log('forking cluster node ' + i);
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
// Workers can share any TCP connection
// In this case its a HTTP server
console.log('i am worker #' + cluster.worker.id);
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
}
/********************************** Cluster **********************************/
// routing
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
io.sockets.on('connection', function (socket) {
// when the client emits 'sendchat', this listens and executes
socket.on('sendchat', function (data) {
// we tell the client to execute 'updatechat' with 2 parameters
io.sockets.emit('updatechat', socket.username, data);
});
socket.on('pm', function(to, message) {
var id = users[to].getSocketID();
io.sockets.socket(id).emit('updatePrivateChat', socket.username, message);
});
socket.on('staffMessage', function(to, message) {
console.log("message to " + to + ": " + message);
var id = users[to].getSocketID();
io.sockets.socket(id).emit('updateStaffChat', socket.username, message);
});
// when the client emits 'adduser', this listens and executes
socket.on('adduser', function(username){
if(isValidUsername(username) && users[username] == null){ // user does not already exist
// we store the username in the socket session for this client
socket.username = username;
// add the client's username to the global list
users[username] = new User(socket.id, socket.username, Roles.user);
console.log('added user ' + username + '. now we have ' + Object.keys(users).length + " users");
// echo to client they've connected
socket.emit('updatechat', 'SERVER', 'you have connected');
// echo globally (all clients) that a person has connected
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has connected');
// update the list of users in chat, client-side
io.sockets.emit('updateusers', getUsernames());
}
else{
socket.emit('usernameExists', username);
}
});
// when the user wants to chat with staff
socket.on('contactStaff', function(){
/***************** search for a free staff ***************/
var member = null;
console.log('user ' + users[socket.username].getUsername() + ' wants to contact staff');
var staffList = getStaffObjects();
if(Object.keys(staffList).length == 0)
console.log('error. no staff available');
else {
var staffNumber = Math.floor(Math.random() * (Object.keys(staffList).length-1));
var i = 0;
console.log("StaffList Length: " + Object.keys(staffList).length);
for(key in staffList) {
if(i == staffNumber) {
member = staffList[key]
staffname = member.getUsername();
break;
}
i++;
}
console.log('adding ' + socket.username + ' to ' + member.getUsername());
member.addUser(users[socket.username]);
console.log('new contact for staff member. ' + member.getUsername() + ' has now ' + member.getUserCount() + ' contacts');
console.log('staffFound: ' + member.getUsername() + ", user: " + socket.username);
io.sockets.socket(users[socket.username].getSocketID()).emit('staffFound', member.getUsername());
io.sockets.socket(users[socket.username].getSocketID()).emit('updateStaffChat', member.getUsername(), "Sie haben einen Mitarbeiter kontaktiert. Was kann ich für Sie tun?");
io.sockets.socket(member.getSocketID()).emit('staffFound', socket.username);
io.sockets.socket(member.getSocketID()).emit('updateStaffChat', socket.username, "Sie wurden von dem Benutzer " + socket.username + " kontaktiert");
}
});
// register a staff member
socket.on('registerAsStaff', function(password){
if(password != "abc")
return false;
var staffname = socket.username;
users[socket.username].setRole(Roles.staff);
console.log('new staff member: ' + socket.username + " with role: " + users[socket.username].getRole());
});
// when the user disconnects.. perform this
socket.on('disconnect', function(){
// remove the username from global usernames list
users[socket.username] = null;
delete users[socket.username];
// update list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
// echo globally that this client has left
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
});
});
My problem:
The chat ist working. Clustering itself is working, but of cause I need a correct redis integration. Could anyone help me on how to do that? Is my solution in general a performant solution for a big amount of clients (max. 1000-2000 in the first step)? - I know I will have to benchmark testing and so on, but is there anything you see that would prevent me from doing that?
I also need a solution to have multiple instances lateron. I found that description on http://blog.cloudfoundry.com/2013/01/24/scaling-real-time-apps-on-cloud-foundry-using-node-js-and-redis/. I understand the problem of socket.io with multiple instances, but I don't understand the implementation. Could anybody please provide a short example on how to do this?

Resources