socket.io: how to make server run functions when disconnection from a room - node.js

Trying to learn socket io and I'm playing around with a test application where:
Sockets join different rooms depending on the page url (/page1 connections join room page1, etc)
I have a separate 'dashboard' page which I want to show how many connections are from each page
I have two problems (the documentation is confusing me):
When a user disconnects, I don't know how to determine which room they were in and hence do different functions depending on the room.
Here is my attempt:
Server Code
io.on('connection', function(socket){
socket.on('joinRoom', function(room){
console.log('New connection to room: ' + room);
socket.join(room); // Clients join different rooms depending on the page.
console.log(socket.rooms); // socket.rooms here has a value
// code to increment room counter will go here.
});
socket.on('disconnect', function(){
console.log(socket.rooms); // In this line, socket.rooms is EMPTY
if (socket.rooms == room1){
// decrement room 1 counter
}
});
});
Client Code
var socket = io();
socket.on('connect', function(){
socket.emit('joinRoom', 'client');
});
My next problem, I can't seem to get the right syntax for the client to emit to a room / the server to react to messages coming from a specific room. If someone could give me an example to follow it would be extremely helpful.
Thanks for reading.

Upon join you need to "register" the room for the socket like the following:
currentRoom[socket.id] = room.newRoom;
and when they leave just call:
socket.leave(currentRoom[socket.id]);
this should do.

Related

socket.io room size 4.x

So i've been making a realtime E2EE chatting application in node.js and socket.io, and I've ran into an issue
I'm using a recent socket.io version, specifically 4.2.0 and I've been attempting to find out the number of clients within a room, in order to create a check to either allow, or block access to a chat area, since it doesn't support more than 2 clients in 1 room.
My variables/includes for socket.io
var socket = require("socket.io")
var io = socket(server)
var chat = io.of('/socket').on('connection', function (socket) {
socket.on('loginchat', async function(data){
io.in(data.id).allSockets().then(result=>{
console.log(result.size) })
});
});
data.id is a randomly generated string and used to create a room server side, so my idea was to use allSockets() to get a count of sockets connected to that room, but it returns zero even with 2 clients connected.
allSockets() idea from https://stackoverflow.com/a/66065685/16140221
I can't find a working solution on stack overflow, or anything inside the docs that is an obvious method to do so. I'm not the best at socket.io since I simply picked it up to use for this project, so any help is appreciated :)
Given the example you provided, you don't seem to ever make a socket to join a room. So your room is forever empty.
According the socket.io documentation you must first listen for the connection event, then you must use join to add users to the room.
const socket = require("socket.io")
const io = socket(server)
io.on("connection", socket => {
socket.on('loginchat', async data => {
//Assuming data.id is the room identifier
const roomUsers = await io.in(data.id).allSockets();
//There, you make your socket join the room if room users are not exceeding
//your maximum
if(roomUsers.size < 2) socket.join(data.id);
});
});
var socket = require("socket.io")
var io = socket(server)
var chat = io.of('/socket').on('connection', function (socket) {
socket.on('loginchat', async function(data){
const sockets = await io.of("/socket").in(data.id).fetchSockets();
console.log(sockets.length)
if(sockets.length <= 1){
socket.room = data.id;
socket.join(data.id);
}else{
//attempting to join a room with greater than 2 users
console.log("too many")
}
});
});
https://socket.io/docs/v3/migrating-from-3-x-to-4-0/
Using the fetchSockets() and checking the length of the object, you are able to check the length or numbers of users in a room.
Working in Socket.io v4.3.1 but most likely functioning in any v4.x

socket.io switching namespaces

I am currently working on a simple chat with socket.io. The basics are already working , but now I am trying to implement 2 different namespaces. I want the client to be able to toggle from one namespace (support-chat) to the other (friends-chat) by a buttonclick.
Serverside
//default namespace
io.on('connection', function(socket){
console.log('a user connected to the chat');
socket.on('disconnect', function(){
console.log('user disconnected');
});
socket.on('client message', function(msg){
io.emit('server_message', msg);
});
});
//namespace /support
var sup = io.of('/support');
sup.on('connection', function(socket){
console.log('someone entered the support-chat');
socket.on('disconnect', function(){
console.log('user disconnected from support-chat');
});
//recieving and emitting message to all clients in namespace /support
socket.on('client message', function(msg){
console.log('message received: ' + msg);
io.of('/support').emit('server_message', msg);
});
});
//namespace /friends
var frnd = io.of('/friends');
frnd.on('connection', function(socket){
console.log('someone entered the friends-chat');
socket.on('disconnect', function(){
console.log('user disconnected from friends-chat');
});
//recieving and emitting message to all clients in namespace /friends
socket.on('client message', function(msg){
console.log('message received: ' + msg);
io.of('/friends').emit('server_message', msg);
});
});
Clientside
var socket = io.connect();
//toggle namespace
$("#support_button").click(function(){
socket.disconnect();
socket = io('/support');
$('#messages').append($('<li>').text("You entered the Support-Chat"));
});
//toggle namespace
$("#friends_button").click(function(){
socket.disconnect();
socket = io('/friends');
$('#messages').append($('<li>').text("You entered the Friends-Chat"));
});
//sending message on submit
$('form').submit(function(){
socket.emit('client message', $('#m').val());
$('#m').val('');
return false;
});
//recieving message and display
socket.on('server_message', function(msg){
$('#messages').append($('<li>').text(msg));
});
});
I think the switch itself is working, because the connection- and disconnect-events are triggering like they should. But when it comes to emitting the message (the server already recieved from the client) to everyone in the same namespace, it is not working.
Isnt this the serversided call for emitting in a specific namespace?:
io.of('namespace').emit();
Do I misunderstand the usage of namespaces? I wanted to implement rooms right after the namespace-"split" of the 2 mainchats for support and friends.
Or did I implement the namespaces wrong on the serverside? I thought io.on(..), io.of('/support').on(..) and io.of('/friends').on(..) are all working the same way and catch the events of their own namespaces-clients.
Any help is highly appreciated! I feel like namespaces have been kind of neglected in there "basic-usage" documentations.
You can't "switch" namespaces on an existing connection. You connect to a specific namespace when the connection is made and once made, it can't be changed.
You could drop the current connection and connect to a new namespace with a new connection. But, given your application, you're misusing the concept of namespaces if you want to switch namespaces and should be using rooms instead.
For rooms, the client can send the server a request to switch rooms and the server can then remove the user from an existing room and add them to a new room. Then, from the server, you can easily broadcast to all connections in a given room.
In fact, rooms were invented around the concept of chat (though they have many other uses) so they are perfectly suited for the chat rooms that you wish to implement.
Namespaces are a heavier weight division than rooms. A connection must connect to a specific namespace when the connection is made and that cannot be changed during the connection.
Rooms on the other hand are a lot more flexible. The server can put a given connection add or remove a connection from a room at any time and a connection can even be in more than one room.
Both rooms and namespaces support broadcasting to all users in that collection.
I think of namespaces more like channels of functionality. So, I'd like to connect to the "price" change namespace in order to get notifications of price changes or I'd connect to the "system" namespace in order to get alerts about things happening in the system or to send messages to manage things in the system.
Whereas rooms are arbitrary collections of users interested in sharing information and I may be in more than one room.

broadcast client disconnect to a particular room

I am following this excellent Tutorial to make my first test game in node.js
Everything is working perfectly. Even when I changed it from two player to three player, still worked fine. But unfortunately the tutorial did not cover the disconnect event. Every event on server is happening against some client action. e.g join game, answer click etc.
But i am stuck at broadcasting to all clients in a room when one of the user looses his internet connection because he performs no action to do that. By some search i found something like following
// Listen for Socket.IO Connections. Once connected, start the game logic.
io.sockets.on('connection', function (socket)
{
//console.log('client connected');
initGame(io, socket);
io.sockets.on('disconnect', function ()
{
socket.broadcast.emit('playerDisconnected', 'x has disconnected');
});
});
This is not working. And I cant understand how to know and how to tell users in a room that who has disccented. Please guide me what i can do more to make question clear.
Edit
With the help of given answer I am able to broadcast a disconnect event to all clients connected to the server. But what can i do to emit only to the particular game clients mean from where i could get gameid/roomid in this event. I see something like game socket in my working code/ (following is it). but I tried simple socket.emit('playerDisconnected', 'x has disconnected'); it does not work and emit.broadcast works but not as i need
function initGame(sio, socket)
{
io = sio;
gameSocket = socket;
gameSocket.emit('app_ev_connected', { message: "You are connected!" });
gameSocket.on('serv_ev_playerJoinGame', playerJoinGame);
gameSocket.on('serv_ev_getRooms',getRooms);
}
Don't you need to add the disconnect event on the socket itself?
io.sockets.on('connection', function (socket) {
initGame(io, socket);
socket.on('disconnect', function () {
//socket.broadcast.emit('playerDisconnected', 'x has disconnected');
socket.broadcast.emit('playerDisconnected', this.id + ' has disconnected');
//if you want to emit in particular room
io.sockets.in('roomName').emit('playerDisconnected', this.id +' has disconnected');
});
});
I had a suggestion which makes more sense in a comment but since I don't have enough reps I couldn't add it.
So I use the userData from my JWT authentication middleware where I put the room number and regenerate the token where I push the room details too!
More on SocketIO Middleware
More on JWT Auth

socket.io Detect what room a user has disconnected from

I have found a few solutions but they seem dates and are not working.
How can I detect what room a user was disconnected from?
I don't know what do you mean about room , but if it is a chat app or something like that
you can create a variable on socket object like roomID when first connection.
io.sockets.on("connection",function(socket){
socket.on("new:user",function(userDATA){
// create variable as roomID on socket.
socket.roomID = userDATA.roomID
});
// other events
socket.on("disconnect",function(data){
var roomID = socket.roomID;
// do what you want to do
});
});
With socket.io 1.0 you can do this with following code:
socket.on('disconnect', function() {
console.log(socket.rooms);
});
It will output an array of rooms the user was joined to.
Note that each socket automatically joins a room named with this socket's ID. So you might see something like
[ 'hIP6r4z8Ym1n5SQUAAAA', 'my_room_name' ]
in your console.

Hosting multiple instances of a node.js server

I'm new to node.js and I'm working on learning how to use Socket.io to create multiple chat servers on my domain.
Here's the scenario:
you log onto the site
you pick a chat room or create a chat room
you join that individual chat room while other chat rooms are going on at the same time
Pretty standard operation on the web but I have yet to find a way to do it. Specifically, how to host it on your domain.
When creating and testing I always just use my localhost and tell the server to listen(8000) . However, how do write a script that:
A) creates a new listening port dynamically for each new chat sever?
B) how do I host it (I use Hostmonster)?
Instead of creating a separate server for each chat room, you could run all of them from the same server and just maintain a map of chat room name to the sockets involved in it.
For example,
//store a map of chat room name to sockets here
var chatRooms = {};
io.sockets.on('connection', function (socket) {
//when someone wants to join a chat room, check to see if the chat room name already exists, create it if it doesn't, and add the socket to the chat room
socket.on('joinChatRoom', function (data.message) {
var chatRoomName = data.message;
chatRooms[chatRoomName] = chatRooms[chatRoomName] || [];
chatRooms[chatRoomName].push(socket);
//set the chatRoomName into the socket so we can access it later
socket.set( "chatRoomName", chatRoomName, function() {
//when we receive a message
socket.on( "chatMessage", function(data) {
var chatMessage = data.message;
//figure out what chat room this socket belongs to
socket.get( "chatRoomName", function(err,chatRoomName) {
//iterate over the sockets in the chat room and send the message
chatRooms[chatRoomName].each(function( chatRoomSocket ) {
chatRoomSocket.emit("chatMessage", { message : chatMessage } );
});
});
});
});
});
});
Note, this code is untested and is just an idea (you should probably treat it more like pseudocode). There are a bunch of things it doesn't handle like cleanup upon disconnects, errors, etc. There are probably lots of other (and better) ways to accomplish this too but hopefully it'll give you some more ideas.

Resources