socket.io determine if a user is online or offline - node.js

We can trace if a connection is established or disconnected by this code
console.log('a user connected');
socket.on('disconnect', function () {
console.log('user disconnected');
});
Well, its fine. But how can we determine what user connected or gone offline. My client is written in PHP/HTML so they have a user ID.

If your clients have specific user IDs they need to send them to socket.io server. E.g. on client side you can do
// Browser (front-end)
<script>
const socket = io();
socket.emit('login',{userId:'YourUserID'});
</script>
And on server you will put something like
// server (back-end)
const users = {};
io.on('connection', function(socket){
console.log('a user connected');
socket.on('login', function(data){
console.log('a user ' + data.userId + ' connected');
// saving userId to object with socket ID
users[socket.id] = data.userId;
});
socket.on('disconnect', function(){
console.log('user ' + users[socket.id] + ' disconnected');
// remove saved socket from users object
delete users[socket.id];
});
});
Now you can pair socket ID to your user ID and work with it.

In addition of #galethil's answer, What if user opens more than one tab (socket connection), each tab (socket connection) has unique socket id for single user, so we need to manage array of socket ids for particular user,
Client Side Connection:
Support Socket IO Client v3.x,
<!-- SOCKET LIBRARY IN HTML -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.5/socket.io.js"></script>
const host = "http://yourdomain.com";
// PASS your query parameters
const queryParams = { userId: 123 };
const socket = io(host, {
path: "/pathToConnection",
transports: ['websocket'], // https://stackoverflow.com/a/52180905/8987128
upgrade: false,
query: queryParams,
reconnection: false,
rejectUnauthorized: false
});
socket.once("connect", () => {
// USER IS ONLINE
socket.on("online", (userId) => {
console.log(userId, "Is Online!"); // update online status
});
// USER IS OFFLINE
socket.on("offline", (userId) => {
console.log(userId, "Is Offline!"); // update offline status
});
});
Server Side Connection: Support Socket IO Server v3.x,
Dependencies:
const _ = require("lodash");
const express = require('express');
const app = express();
const port = 3000; // define your port
const server = app.listen(port, () => {
console.log(`We are Listening on port ${port}...`);
});
Connection:
const io = require('socket.io')(server, {
path: "/pathToConnection"
});
let users = {};
io.on('connection', (socket) => {
let userId = socket.handshake.query.userId; // GET USER ID
// CHECK IS USER EXHIST
if (!users[userId]) users[userId] = [];
// PUSH SOCKET ID FOR PARTICULAR USER ID
users[userId].push(socket.id);
// USER IS ONLINE BROAD CAST TO ALL CONNECTED USERS
io.sockets.emit("online", userId);
// DISCONNECT EVENT
socket.on('disconnect', (reason) => {
// REMOVE FROM SOCKET USERS
_.remove(users[userId], (u) => u === socket.id);
if (users[userId].length === 0) {
// ISER IS OFFLINE BROAD CAST TO ALL CONNECTED USERS
io.sockets.emit("offline", userId);
// REMOVE OBJECT
delete users[userId];
}
socket.disconnect(); // DISCONNECT SOCKET
});
});
GitHub Demo

We can identify the socket id in server who is connected and who is disconnected. So you can do something like this. You can use this setup if you have an identifier in client side
CLIENT
socket.emit('login', userId);
SERVER SIDE
const users = {};
io.on("connection", (socket) => {
socket.on("login", (data) => {
users[socket.id] = data;
});
socket.on("disconnecting", (reason) => {
delete users[socket.id]; // remove the user. -- maybe not the exact code
});
});
Hope you get the idea.

Related

node js xhr polling too much time 25 30 seconds

I am building an application in this application i add new item for different events from Admin Panel and object just populated by node js socket on frontend and i am getting in few polling request getting two much time.
:4000/socket.io/?EIO=3&transport=polling&t=N6EC8Z7&sid=gKbo2MATsLn2BqtpAAAg
setting i used:-
var io = require('socket.io')(server, { wsEngine: 'ws' });
io.on('connection', function(socket){
console.log('a user connected');
socket.on('disconnect', function(){
console.log('user disconnected');
});
socket.on('JoinEventOneRoom', function(data) {
var id = data.id;
var room = 'notify_event_one_list_'+id;
console.log('Add event one Room Joined sucessfully', room);
socket.join(room);
});
socket.on('JoinEventTwoRoom', function(data) {
var id = data.id;
var room = 'notify_event_two_'+id;
console.log('Delete Event Two Joined sucessfully', room);
socket.join(room);
});
});
React js code
const socket = SocketIOClient(WSC.nodeBaseURL, { transports: ['polling'] });
socket.connect()
Need help what i am missing here.
I am using 2.3.0 version for both socket.io and socket.io-client

emitting both webcam stream and chat to specific room using socket.io nodejs

I have successfully implemented streaming webcam and chat at the same time using node.js and socket.io, I have an emit.html page for whoever is initiating the stream, and a visualize.html page for the clients or recipient, and but I have challenge in attaching webcam stream to only room which is created by the administrator, please how can I emit the webcam to a specific room only.
This is my server code below
const express = require("express");
const app = new express();
const http = require("http").Server(app);
const io = require("socket.io")(http);
const formatMessage = require("./utils/messages");
const {
userJoin,
getCurrentUser,
userLeave,
getRoomUsers,
} = require("./utils/users");
const port = process.env.PORT || 3000;
app.use(express.static(__dirname + "/public"));
const botName = "Admin";
app.get("/", function (req, res) {
res.redirect("index.html");
});
io.on("connection", function (socket) {
socket.on("stream", function (image) {
socket.broadcast.emit("stream", image);
});
//Join Room
socket.on("joinRoom", ({ username, room }) => {
const user = userJoin(socket.id, username, room);
socket.join(user.room);
// Welcome current user
socket.emit("message", formatMessage(botName, "Welcome to Class!"));
// Broadcast when a user connects
socket.broadcast
.to(user.room)
.emit(
"message",
formatMessage(botName, `${user.username} has joined the class`)
);
// Send users and room info
io.to(user.room).emit("roomUsers", {
room: user.room,
users: getRoomUsers(user.room),
});
});
// Listen for chatMessage
socket.on("chatMessage", (msg) => {
const user = getCurrentUser(socket.id);
io.to(user.room).emit("message", formatMessage(user.username, msg));
});
// Runs when client disconnects
socket.on("disconnect", () => {
const user = userLeave(socket.id);
if (user) {
io.to(user.room).emit(
"message",
formatMessage(botName, `${user.username} has left the class`)
);
// Send users and room info
io.to(user.room).emit("roomUsers", {
room: user.room,
users: getRoomUsers(user.room),
});
}
});
});
http.listen(port, () => console.log(`Server running on port ${port}`));
Please check the git repo for the full project, I don't mind any adjustments to make it better.
https://github.com/timotech/screen-cast
Thanks
I was able to resolve the issue by adding the stream socket to the joinRoom socket
This is the adjustment of the code:
socket.on("joinRoom", ({ username, room }) => {
const user = userJoin(socket.id, username, room);
socket.join(user.room);
//Display webcam to user
socket.on("stream", function (image) {
socket.broadcast.to(user.room).emit("stream", image);
});
…
Now everything works as expected. But the only challenge i'm having now is that I hosted it on Heroku, but it doesn't work as expected.
This is the link to the Heroku hosting.
For Teachers: https://screencastst.herokuapp.com/
For Students access: https://screencastst.herokuapp.com/students.html
Teachers are the only ones having access to camera, that's why teachers have a different link and students can only see whichever teacher that is teaching their class, that's also why the difference for the link
But the chat is not working, that is the challenge I have now. But works perfectly on localhost
Thanks

Socket.IO server.origins always returns '*' as the origin value (Nodejs)

I am trying to build a two way socket.io server/client connection. The server will remain behind one IP/domain and the client will behind a different IP. The point is to notify me when the server goes offline, in case of power outage or server failure. The issue I am having, is I am trying to secure the socket so not just anyone can connect to the socket. Socket.IO has a server.origins function that will return the origin of socket trying to connect. Their API documentation explains it like this.
io.origins((origin, callback) => {
if (origin !== 'https://foo.example.com') {
return callback('origin not allowed', false);
}
callback(null, true);
});
The issue I am having is whenever I connect to the socket.io server with socket.io-client the origin is always '*'.
Under potential drawbacks in there API is says:
"in some situations, when it is not possible to determine origin it may have value of *"
How do I get socket.io it see the IP where the socket connection request is coming from?
Once the connection is established I can use the socket information and see the IP where the socket lives, but the connection is already made. I am trying to stop rouge connections.
# Server
const express = require('express');
const app = express();
const chalk = require('chalk')
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const cors = require('cors');
const port = 4424;
app.use(cors());
io.origins((origin, callback) => {
console.log(origin);
if (origin !== '*') {
return callback('origin not allowed', false);
}
callback(null, true);
});
io.on('connection', (socket) => {
console.log('Client connected...');
socket.on('join', (data) => {
console.log(data);
socket.emit('messages', 'Hello from server');
});
})
server.listen(port, () => console.log(chalk.blue(`Express started on port ${port}!`)));
Client:
# Client
const io = require('socket.io-client');
const socket = io('https://"MY DOMAIN THAT THE SERVER IS BEHIND"', { reconnect: true })
socket.on('connect', (data) => {
console.log("Connection successful");
socket.emit('join', 'Hello World from client');
});
socket.on('connect_error', (error) => {
console.log("Connection error");
});
socket.on('disconnect', (timeout) => {
console.log("Connection disconnected");
})
socket.on('messages', (data) => {
console.log(data);
});
I have the server behind a NGINX server using SSL, and connected to the server with the client on a different IP and it goes through and creates the connection, but the Origin is always "*".
Actually I found out you can use middleware with Socket.io with the io.use() function. I just wrote a simple middleware that checks the incoming socket ip with a list of approved ips.
io.use((socket, next) => {
const ip = socket.handshake.headers['x-forwarded-for']
if (firewall(ip))
{
return next();
}
})
And firewall is a function that checks if the ip is in the array of approved ips.

Socket.io multiple response instead of one

I'm facing a problem with socket.io and node js.
Context
I have two servers, one of them is processing heavy jobs and the other is responding to the clients.
The main case is the following one :
The client request data
The "mid server" looks if I have this data in the database. If I haven't, itsend a request to the second server
The second server performs the research.
Once its done, the second server push de data to the "mid server"
The mid server finally push the data to the client (and persists it for future client requests)
Here's the sample code
Client
<script type="text/javascript"/>
var socket = io('https://localhost:9091', {'forceNew': true);
// send a request to the mid server
socket.emit('request', data);
socket.on('response', async (response) => {
// when the mid server responds, the response is printed
$('#container').append(response);
});
</script>
Mid server
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
// in order to use this server as a Socket.io client
const secondServer = require('socket.io-client').connect('http://localhost:9092');
app.set('view engine', 'ejs');
app.get('/', (req, res) => {
res.render('index', {})
});
io.on('connection', async (socket) => {
console.log('User connected');
// On client request
socket.on('request', async (data) => {
console.log('Requesting from ' + socket.id);
// The same request is transmited to the second server
secondServer.emit('request', data);
});
// Once the second server has finished his job
secondServer.on('response', async (data) => {
console.log('Responding for ' + socket.id);
// I send the data back to the client
socket.emit('response', data);
});
socket.on('disconnect', () => {
socket.disconnect();
});
});
// port is 9091
http.listen(port, () => {
console.log('Server listening on port ' + port);
});
Second server
const io = require("socket.io").listen(9092);
io.on("connection", function (socket) {
socket.on('request', async () => {
// await in order to wait the heavyJob to be done before sending the response
var data = await heavyJob()
// Send back the data to the mid server
socket.emit('response', data);
});
});
Problem
The problem I'm facing is, if I refresh the client page, the mid server will send twice the data, once to the old socket and the once for the new one as so :
I have also tried to respond to the client with socket.emit('response', data) on mid server side and socket.on('response', (data) => {}) on client side instead of using callback function. It doesn't change anything.
Do I misunderstanding something ?
Thanks for your help
Edit
It doesn't only happen when the client is refreshing his page. It happens when two different clients send a request at the same time. The server is responding four times, two times for each client.
You are right Nico, I didn't recognized callback can't be reached.
It was my mistake.
According to your edited code, you can pull out "secondServer.on('response'.." from "io.on('connection'"'s callback.
You can try below and I hope this would be helpful.
Mid Server
io.on('connection', async (socket) => {
console.log('User connected');
// On client request
socket.on('request', async (data) => {
console.log('Requesting from ' + socket.id);
// The same request is transmited to the second server
// give data and socket.id to secondServer.
secondServer.emit('request', {data:data, id:socket.id});
});
// Once the second server has finished his job
socket.on('disconnect', () => {
socket.disconnect();
});
});
secondServer.on('response', async (reply) => {
const {id, data} = reply;
console.log('Responding for ' + id);
// I send the data back to the client
io.to(id).emit('response', data);
});
Second Server
const io = require("socket.io").listen(9092);
io.on("connection", function (socket) {
socket.on('request', async (req) => {
// await in order to wait the heavyJob to be done before sending the response
const {id} = req;
var data = await heavyJob();
const reply = { id, data };
// Send back the data to the mid server
socket.emit('response', reply);
});
});
I think you need pull out "secondServer.on('response'.." code from "socket.on('request',..." callback.
io.on('connection', async (socket) => {
console.log('User connected');
// On client request
socket.on('request', async (data, callback) => {
console.log('Requesting from ' + socket.id);
// The same request is transmited to the second server
secondServer.emit('request', data);
});
secondServer.on('response', async (data) => {
console.log('Responding for ' + socket.id);
callback(data.images);
});
});

emitting to single client socket io 1.3.2

I've looked at several answers on here, but I think they are referring to older versions of socket.io as their solutions have not worked for me. I'm getting the data back in the browser with
io.emit('update', data)
but it's emitting to all clients so the same data is showing up in multiple windows when I go to the same URL. Do I have to store the client id somewhere upon connection or can I just get it back before emitting? Please be specific. I tried a few other solutions from SO, but I got a lot of ReferenceError 'id' is not defined or sockets instead of socket.
Server set up and connection:
var app = express();
var server = require('http').createServer(app)
var io = require('socket.io')(server)
app.get('/aPath', function (req, res, next) {
res.writeHead(200)
var data = {
"val1": req.query.val1,
"val2": req.query.val2,
"val3": req.query.val3,
"val4": req.query.val4,
"val5": req.query.val5,
"val6": req.query.val6,
}
/*console.log(io.sockets.id)*/
//io.to(io.sockets.id).emit('update', data)
//io.sockets.socket(id).emit('update', data)
io.emit('update', data)
res.end("OK")
})
io.on('connection', function (socket) {
console.log('websocket user connected')
});
Since a third-party client is sending the info via a restful interface, you will need to include reference data for the client in that request in the form of a header or query string.
I suggest using Redis to store the active socket users for quick reference. This will allow you to have multiple applications in deployment that use a singular redis instance to keep the data in sync. You can also do the same in app memory, but that just doesn't scale well.
first, you need to use middleware to authenticate user and cache the socket.id
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var redis = require('redis');
io.use(function(socket, next){
// validate user
// cache user with socket.id
var userId = validatedUser;
socket.handshake.userId = userId;
redis.set(userId, socket.id, function (err, res) {
next()
});
});
next handle all socket communication
io.on('connection', function (socket) {
console.log('websocket user connected');
//next handle all socket communication
socket.on('endpoint', function (payload) {
//do stuff
socket.emit('endpoint.response', {/*data*/});
});
//Then remove socket.id from cache
socket.on('disconnect', function (payload) {
//remove user.id from cache
redis.del(socket.handshake.userId, function (err, res) {
console.log('user with %s disconnected', socket.id);
});
});
});
Handle third party event.
app.get('/aPath', function (req, res, next) {
// get user from third party
var userId = req.query.userId
var data = {
"val1": req.query.val1,
"val2": req.query.val2,
"val3": req.query.val3,
"val4": req.query.val4,
"val5": req.query.val5,
"val6": req.query.val6,
};
// get cached socketId from userId
redis.get(userId, function (err, socketId) {
// return ok to third party;
res.status(200).send("OK");
only emit if socketid still exists
if (err || !socketId) return;
// now emit to user
io.to(socketId).emit('update', data):
});
});

Resources