My goal is when A user click on B user, they'll join in a room. I'm holding socket.id in array for all clients.
//CLIENT SIDE FOR A//
$('.chat-start').on('click', function(event) {
event.preventDefault();
var room_id = '72xaz132s'
var target_id = $('.target').data('id');
socket.emit('force join room', room_id, target_id);
});
//SERVER SIDE FOR A AS AN EXAMPLE//
var clients {'customIDA' : socket.id for a, 'customIDB' : socket.id for b}
socket.on('force join room', function (roomid, customIDB) {
socket.join(chat_id); //CLIENT A JOINS ROOM//
})
Client A joins room without problem, but how can i add also client B on same room ?
In io.on("connection") you can store socket object with user id as a object
{"socket": socket, "id": user_id}
to array. Then find index from array:
let user_index = users.findIndex(user => user.id == user_id);
And finally join to room.
let socketB = users[user_index].socket;
socketB.join('room');
You can call join to subscribe the socket to a given channel:
// On connection join room
io.on('connection', function(socket){
socket.join('ROOM ID');
});
And then simply use to or in (they are the same) when broadcasting or emitting:
io.to('some room').emit('some event');
To leave a channel you call leave in the same fashion as join.
Documentation: https://socket.io/docs/rooms-and-namespaces/
function join() {
// Run's once clicked
console.log('Join function here');
}
Join
Maybe handle it server side? Have a dictionary that stores User Ids and the Socket Id, you can store this information io.on("connection"). Then when User A does the process of creating the room, send to the server side the room id and something that identifies User B. Server side figures out the socket id of User B and joins them to the room.
This method is a bit of a hack, but it does allow you to force any specified socket into a room entirely server-side, once you have their socket ID, without maintaining another object array. SocketIO (v2.x) stores room allocations in two places to make things fun.
// $targetSocket is the socket ID of the connection you want to force into a room
$roomArray = $io->sockets->connected[$targetSocket]->rooms; //socket's rooms stored here
$roomArray[$newRoom] = $newRoom; // add the new room to this array
$io->sockets->connected[$targetSocket]->rooms = $roomArray; // save the modified array
$io->sockets->adapter->add($targetSocket, $newRoom); // update the adapter too
This is obviously PHP but it will translate easily into js.
You can do the reverse to kick users out of rooms too:
unset($roomArray[$oldRoom]); //delete room key and value from this socket's array
// save the array here, see above
$io->sockets->adapter->del($targetSocket, $oldRoom); // delete from adapter
Use both at once for a leave and join system.
This may seem pointless when a socket can add or remove itself so easily with socket->join('ROOM'); but there are circumstances in which you may need to do this from outside the connection's socket, such a game function.
For anyone facing a similar issue.
#Kouvolan Arnold's solution will work but having to store the socket object for each user is too much of a resource, especially when socket.io already stores them.
A simple solution here is for you to get the socket object already stored by locating it. The good news is that socket.io already provides that, see below.
//v2.x or less
io.sockets.sockets[USER_SOCKET_ID_HERE]
//v3.x and 4.x
io.sockets.sockets.get(USER_SOCKET_ID_HERE)
The code below would have worked well in your case.
//v2.x or less
io.sockets.sockets[clientSocketId].join(roomid);
//v3.x and 4.x
io.sockets.sockets.get(clientSocketId).join(roomid);
Thanks
Related
I'm trying to store all user's data in an array with socket.io, but when i put a new information, the last one is deleted.
var userInformations = <{}[]>[];
socketInfo.on('putUserInformations', (userData:{}) => {
userInformations.push(userData)
console.log(userInformations)
})
Is it that you want to store something about what the user is doing in a particular room? There are two ways to do this, either store it on the socket ID, or store it in the room.
The way I've done this before is as follows. For storing data about the room, I attach it to the room ID by adding new keys to the room object. While the room remains active, this data will remain actively attached to the room, so you can easily access it through your code
io.sockets.adapter.rooms[ROOM_ID_GOES_HERE].lockedInput = { "some_data" : 1 }
I'm not sure how you've done this but you can test out this stuff by console.log(io.sockets.adapter.rooms) to see what you have there. For storing information about the socket user, you can just attach this directly to the socket, i.e.
io.on('connection', function(socket) {
socket.user = 'User 1';
});
One is private to that particular socket, and one is private to a particular room. Both of these allow you to gather information on the room/user and it will persist while the socket is active. When the socket/room are both closed, the data will not persist, but you can store it in a database and pull it in when the user reconnects if you need to.
So, let's say I have an array of few socket ids (['RZ0_7yBdwyvlT8-bAAAA', 'iyeiRpVdmzAQSWyTAAAB', 'kSd2Iudt9SV29w9HAAAC']). On particular trigger, i want to move everyone from array to one socket.io room so i can emit events only to them. How can I do that??
You first need to get socket object from socketId and then call join("room_name") on it.
Let say room name is random.
var room_name = "random";
var ids = ['RZ0_7yBdwyvlT8-bAAAA', 'iyeiRpVdmzAQSWyTAAAB', 'kSd2Iudt9SV29w9HAAAC'];
ids.forEach(function(){
io.sockets.connected[id].join(room_name); // for v1.0
// io.sockets.sockets[id].join(room_name); // for V0.9
});
I have a specific socket_id that I want to remove from a room in socket.io. Is there an easy way to do this? The only way I can find is to disconnect by the socket itself.
From the client, you send your own message to the server and ask the server to remove it from the room. The server can then use socket.leave("someRoom") to remove that socket from a given room. Clients cannot join or leave rooms themselves. Joining and leaving to/from rooms has to be done by the server (because the whole notion of chat rooms only really exists on the server).
Documentation here.
For socket.io version 4.0.0 upwards (Mine is version 4.4.0):
Step #1) You have to get an array of the sockets connected to that socket.id as shown in the code below.
//You can replace the userStatus.socketId to your own socket id, that was what I
//used in my own case
const userSockets = await io.of("/chat").in(userStatus.socketId).fetchSockets();
Step #2) After getting all the socket IDs, you will be given an array containing the individual socket (I got to know this by logging the results from the async and await fetchSockets() method that the io library on the server offered). Then use the .find() method to return the single socket object matching that same socket id again as shown below.
const userSocket = userSockets.find(socket => socket.id.toString() === userStatus.socketId);
// Replace userStatus.socketId with your own socket ID
Step #3) Remove the socket by leaving the group using the .leave() method of the socket.io library as shown below.
userSocket.leave(groupId);
// groupId can be replaced with the name of your room
Final Notes: I hope this helps someone in the future because this was what I used and it worked for me.
Here is my connection function where I add the socket to the SOCKET_LIST global so I can iterate through it later.
Player.onConnect does initialization which will create a new room if none are open or join one if someone is waiting for an oponent.
io.sockets.on('connection', function(socket) {
SOCKET_LIST[socket.id] = socket;
Player.onConnect(socket);
});
then this is where I try emitting all the data to users in the same room.. but instead of just emitting the data to people in the room it winds up emitting data to everyone connected to the server.
setInterval(function(){
var pack = {
player:Player.update(),
platform:Platform.update()
}
for (var i in SOCKET_LIST) {
var socket = SOCKET_LIST[i];
io.sockets.in(socket.room).emit('newPositions', pack);
}
}, 30);
When I console.log out the room ids from the server it works accordingly, like i'll join from 2 clients and then the third one will console out a new room id, and then the 5th one, etc. Since the game is suppose to be a 2 player game and a new room is created if someone else tries to join a game.
for (var i in SOCKET_LIST)
loops over your SOCKET_LIST and gets all sockets that is why the server emits to everyone.
I have been searching for this particular problem for the past week, and since I couldn't find any information on the subject(that wasnt outdated), I just decided to work on other things. But now I am at the point where I need to be able to send data(that I constructed) to specific clients using their ID who are connected to my server using node.js and socket.io. I already store the ID in an object for each new connection. What I need to know is a way to just send it to a connection ID I choose.
Something like: function send(data, client.id) {};
I am using an http server, not TCP.
Is this possible?
edit:
server = http_socket.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(respcont);
client_ip_address = req.header('x-forwarded-for');
});
socket = io.listen(1337); // listen
//=============================================
// Socket event loop
//=============================================
socket.on ('connection', function (client_connect) {
var client_info = new client_connection_info(); // this just holds information
client_info.addNode(client_connect.id, client_connect.remoteAddress, 1); // 1 = trying to connet
var a = client_info.getNode(client_connect.id,null,null).socket_id; // an object holding the information. this function just gets the socket_id
client_connect.socket(a).emit('message', 'hi');
client_connect.on('message', function (data) {
});
client_connect.on ('disconnect', function () {
);
});
solution: I figured it out by just experimenting... What you have todo is make sure you store the SOCKET, not the socket.id (like i was doing) and use that instead.
client_info.addNode(client_connect.id, client_connect.remoteAddress, client_connect, 1)
var a = client_info.getNode(client_connect.id,null,null,null).socket;
a.emit('message', 'hi');
If you need to do this, the easiest thing to do is to build and maintain a global associative array that maps ids to connections: you can then look up the appropriate connection whenever you need and just use the regular send functions. You'll need some logic to remove connections from the array, but that shouldn't be too painful.
Yes, it is possible.
io.sockets.socket(id).emit('message', 'data');
Your solution has one major drawback: scaling. What will you do when your app needs more the one machine? Using internal IDs also could be difficult. I advice using external IDs (like usernames).
Similarly to this post I advice using rooms (together with Redis to allow scaling). Add private room for every new socket (basing on user's name for example). The code may look like this:
socket.join('priv/' + name);
io.sockets.in('priv/' + name).emit('message', { msg: 'hello world!' });
This solution allows multiple machines to emit events to any user. Also it is quite simple and elegant in my opinion.