Socket.io disconnect client by id - node.js

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.

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

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

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

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.

Sending message to specific client with socket.io and empty message queue

I´m going crazy with socket.io!
Documentation is so bad it's simply not true.
I want to send a feedback to specific client over socket.io
My server side looks like this:
app.get('/upload', requiresLogin, function(request, response) {
response.render('upload/index.jade');
io.sockets.on('connection', function (socket) {
console.log('SOCKET ID ' + socket.id);
io.sockets.socket(socket.id).emit('new', 'hello');
});
});
and the client side looks like this:
$(document).ready(function() {
var socket = io.connect('http://localhost:80/socket.io/socket.io.js');
socket.on('new', function (data) {
console.log(socket.id);
console.log(data);
//$('#state').html(data.status);
});
});
but the client does simply nothing. I have tried nearly everything. Can someone tell me what I am doing wrong, please! :(
to send a message to a specific client save every one that connects to the server in an Object.
var socketio = require('socket.io');
var clients = {};
var io = socketio.listen(app);
io.sockets.on('connection', function (socket) {
clients[socket.id] = socket;
});
then you can later do something like this:
var socket = clients[sId];
socket.emit('show', {});
A couple of ways to send feedback to a specific client over socket.io include:
As stated by pkyeck, save all clients to an Object, so you can send to these specific clients later in your route handlers, e.g.:
var sessionsConnections = {};
sio.on('connection', function (socket) {
// do all the session stuff
sessionsConnections[socket.handshake.sessionID] = socket;
});
or, use socket.io's built in support for rooms - subscribe each client to a room on connection and send via this room within route handlers, e.g.:
sio.on('connection', function (socket) {
// do all the session stuff
socket.join(socket.handshake.sessionID);
// socket.io will leave the room upon disconnect
});
app.get('/', function (req, res) {
sio.sockets.in(req.sessionID).send('Man, good to see you back!');
});
Acknowledgement:
http://www.danielbaulig.de/socket-ioexpress/#comment-1158
Note that both these example solutions assume that socket.io and express have been configured to use the same session store and hence refer to the same session objects. See the links above and below for more details on achieving this:
https://github.com/LearnBoost/socket.io/wiki/Authorizing
2 things
1) You should really place your io.sockets.on(..) outside your app/update route to prevent adding multiple listeners for clients.
2) io.sockets.socket(id); should not be used, it should have been socket.emit('new', 'hello')
In socket.io 1.0, this is how it would work. It may work for lower versions, but I cannot guarantee it.
socket.to(socket_id_here).emit('new', 'hello');
This works because socket.io automatically adds a socket to a room with the socket's id on connection.
Also, if you plan to upgrade to version 1.0, there are a lot of changes to the api, so you'll sometimes have to change some code to make it work in 1.0.
The correct way to do this in Socket.io 1.0+ is:
io.to(users_socket_id).emit('new', 'hello');
You can also substitute a room name for 'users_socket_id' to emit to a specific socket.io room.
First of all, you cannot use socket.id on client side.
And then change the line
var socket = io.connect('http://localhost:80/socket.io/socket.io.js');
to
var socket = io.connect('http://localhost:80/');
I believe io.sockets.socket has been removed and has been a problem in Socket.IO (https://github.com/socketio/socket.io/issues/1618).
You can use io.sockets.connected[socket.id] and store the socket.id to reference with the user via username:
var usernames = {};
usernames[username] = socket.id;
// Sending the message
io.sockets.connected[usernames[username]].emit(...);
I don't see it anywhere on the documentation so I can see how this hasn't been answered. Also, if you don't have any reference via usernames, you can instead try:
users[socket.id] = socket.id;
to duplicate the key and value for easy access.
There is also possibly another way by using io.clients as described below, but I haven't used that method.
OTHER SOLUTION: Send message to specific client with socket.io and node.js
Have you tried using?
var socket = io.connect('http://localhost:80/');
i tired with the latest version of node and socket.io
below i am going to post complete code
<ul id="messages"></ul>
<form action="">
<input id="id1" /><button>Send</button>
</form>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
var io = io.connect();
$('form').submit(function(){
io.emit('drumevent', $('#id1').val());
$('#id1').val('');
return false;
});
io.on('drumevent', function(msg){
console.log(msg);
$('#messages').append($('<li></li>').text(msg.message+' quantity = '+msg.quantity));
});
</script>
server side code
var usernames = {};io.on('connection', function(socket){usernames["username"] = socket.id;
socket.on('drumevent', function(msg){
var socketId = socket.id;io.to(socketId).emit('drumevent', data);

Resources