broadcast client disconnect to a particular room - node.js

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

Related

socket.io dont update when i use chat rooms

I have tried for 2 hours to get it working with NO success.
const stock = io.of('/stocks');
stock.on('connection', socket => {
When i do that my messages is not updating on the client side..
But when i do this: io.on('connection', socket => {
then it works. But i need to have different chat rooms. And i have no idea why the io.of not works.
I have tried with everything that has come to my head. I dont understand why it just works when i dont use the io.of, it is really weird.
What can i do for it to work correctly?
custom namespace in socket.io, you have to decare it first, then connect.
const nsp = io.of('/my-namespace');
nsp.on('connection', function(socket){
console.log('someone connected');
});
nsp.emit('hi', 'everyone!');

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.

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

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.

Socket.io disconnect client by id

I'm new to nodejs and trying to write a chat room as so many people have.
The chat consists of multiple rooms and clients. Commands such as /nick /join /help /ls users /ls rooms work as you would expect although I'm having trouble with getting a /kick command to work.
I'm just not sure how you disconnect a client by id, so far /kick client is able to present the respective clients socket.id although I'm stuck for the code to kick via socket.id.
Code so far:
Disconnect client who sent /kick: socket.disconnect();
Delete client from arg /kick client: delete io.sockets.sockets[client];
Deleting the client doesn't disconnect them though, they can still receive data just not send it.
Solved
CuriousGuy's 0.9 worked flawlessly, for those interested - here is the code I'm using.
Server side:
handleClientKick(socket);
...
function handleClientKick(socket) {
socket.on('kick', function(client) {
if (typeof io.sockets.sockets[client] != 'undefined') {
socket.emit('message', {text: nickNames[socket.id] + ' kicked: ' + nickNames[client]});
io.sockets.sockets[client].disconnect();
} else {
socket.emit('message', {text: 'User: ' + name + ' does not exist.'});
}
});
}
Client side:
kickClient = function(client) {
this.socket.emit('kick', client);
};
The following code works with Socket.IO 1.0, however I'm not sure that this is the best solution:
if (io.sockets.connected[socket.id]) {
io.sockets.connected[socket.id].disconnect();
}
Update:
With Socket.IO 0.9 the code would be slightly different:
if (io.sockets.sockets[socket.id]) {
io.sockets.sockets[socket.id].disconnect();
}
This is an old question but if anyone wonders for newer versions;
In Socket.IO v4.X io.sockets.connected[socket.id] or io.sockets.sockets[socket.id] is not working.
So we need to do like this;
io.sockets.sockets.forEach((socket) => {
// If given socket id is exist in list of all sockets, kill it
if(socket.id === givenSocketID)
socket.disconnect(true);
});
Alternate solution In Socket.IO v4.X
For all sockets
const sockets = await io.fetchSockets();
For particular socket
const sockets = await io.in(theSocketId).fetchSockets();
Iterate sockets
for (const socket of sockets) {
console.log("socket id",socket.id);
socket.disconnect(true);
}
Reference link
Here's another option for Socket.IO v4 that doesn't require async syntax:
io.sockets.sockets.get(socket.id)
Someone can correct this if it's wrong, but I think each socket has a unique ID, so there should be no need for iterating.

Security on Socket.io

Using Socket.io what is the best way to require some sort of authentication for pushing to the socket. For example I have one group listeners who should only be allowed to received updates and I have admins who are allowed to push updates (and listen).
I could use some sort of passphrase that only the "admin" knows.
I could use separate sockets for pushing vs listening and block all listeners to the pushing port
?
Otherwise the listeners would just need to come up with some creative javascript in order to do the correct push. We can also assume that the server doesn't know if someone is an admin or a listner.
For example:
//Server
socket.on('foo', function (data) {
io.sockets.emit('message', data);
}
//listner client
socket.on('message', function (data) {
alert("admin sent new update");
});
//admin client
someButton.onclick = sendMessage = function() {
socket.emit('foo', {'update'});
};

Resources