Socket.IO: Remove specific socket_id from room? - javascript

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.

Related

matrix-js-sdk setup and configuration

I am having some issues trying to connect to a matrix server using the matrix-js-sdk in a react app.
I have provided a simple code example below, and made sure that credentials are valid (login works) and that the environment variable containing the URL for the matrix client is set. I have signed into element in a browser and created two rooms for testing purposes, and was expecting these two rooms would be returned from matrixClient.getRooms(). However, this simply returns an empty array. With some further testing it seems like the asynchronous functions provided for fetching room, member and group ID's only, works as expected.
According to https://matrix.org/docs/guides/usage-of-the-matrix-js-sd these should be valid steps for setting up the matrix-js-sdk, however the sync is never executed either.
const matrixClient = sdk.createClient(
process.env.REACT_APP_MATRIX_CLIENT_URL!
);
await matrixClient.long("m.login.password", credentials);
matrixClient.once('sync', () => {
debugger; // Never hit
}
for (const room of matrixClient.getRooms()) {
debugger; // Never hit
}
I did manage to use the roomId's returned from await matrixClient.roomInitialSync(roomId, limit, callback), however this lead me to another issue where I can't figure out how to decrypt messages, as the events containing the messages sent in the room seems to be of type 'm.room.encrypted' instead of 'm.room.message'.
Does anyone have any good examples of working implementations for the matrix-js-sdk, or any other good resources for properly understanding how to put this all together? I need to be able to load rooms, persons, messages etc. and display these respectively in a ReactJS application.
It turns out I simply forgot to run startClient on the matrix client, resulting in it not fetching any data.

Is it possible to change the URL request on a WebSocket connection?

I am trying to change the URL request on an already connected socket but I can't figure out how or if it is even possible.
I am working with the WebSocket API and CoinCap.
What I am doing right now is closing the connection and creating a new one with the new parameters.
// Create a new WS connection
const webSocketURL = `wss://ws.coincap.io/prices?assets=${loadedKeys}`
// loadedKeys could be a string of one coin (e.g. bitcoin) or an array
// or an array (e.g. bitcoin,ethereum,monero,litecoin), has to be dynamic.
pricesWs = new WebSocket(webSocketURL);
pricesWs.onopen = function () {
console.log(`conected: ${pricesWs.readyState}`)
}
pricesWs.onmessage = function (msg) {
handleUpdateCB(msg.data);
}
// then when I need to receive different coin prices
// I close the connection and reopen a new one.
anotherFunction() {
pricesWs.close();
pricesWs = new WebSocket(aNewWebSocketURL);
}
I tried sending parameters as messages with send() function without success, I keep receiving the same data, let's say I first connect asking for bitcoin and the I want to receive bitcoin and ethereum I tried this
pricesWs = new WebSocket(`wss://ws.coincap.io/prices?assets=bitcoin);
//then tried
pricesWs.send(bitcoin,ethereum)
this doesn't work, I also tried sending as JSON but I kept getting the same data just for the first query(bitcoin)
UPDATE:
This is the the Git for the app, if you are interested seeing the whole thing together.
Git
UPDATE 2:
I created this pen to make it easier to understand, note that the pen is made on VueJS, but that isn't important. The important part is on line 60 JS panel
Is there any reason why you want to switch the URL?
According to the coin cap documentation, you can request information about multiple crypto currency at once. Is it not an option for you?
Generally you should avoid opening and closing connections to a socket as there is slight latency albeit very insignificant. Leaving the connection open is better since you will be notified if price is changed for any of the currencies you are interested it.
The answer to your original question "Is it possible to change URL for a web socket connection?" is no! You can't change URL however you can create as many connections as you need. In your case you are closing the connection and opening it immediately but in the comments I noticed that you mentioned that it is based on user interaction. You can open connection just for the currencies you care about when user requests it and keep the connection opened until user switches the currency again because at that point you'll probably switch to another currency.
I also agree with #Taylor Spark, you can also just hide the dom for the currencies user don't care and render the ones they are interested in.

Socket.io join another client to a room

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

Node.JS Socket.IO sending packets to specific connection IDs

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.

Namespacing in socket.io when sending to a particular socket

When i namespace my app, i run into a problem i want to send data to a particular socket, here's the abbreviated version of the code I'm using:
var io = require('socket.io').listen(config.web.port);
var chat = io.of('space').on('connection', function (socket) {
// This works:
// Input: xhr-polling received data packet 5::/space:{"name":"test"}
// Output: xhr-polling writing 5::/space:{"name":"test","args":[{"msg":"test"}]}
socket.on('test', function(){
socket.emit('test',{msg: "test"});
});
// This fails:
// Input: xhr-polling received data packet 5::/space:{"name":"test2"}
// Output: xhr-polling writing 5:::{"name":"test2","args":[{"msg":"test2"}]}
socket.on('test2',function(){
io.sockets.socket(socket.id).emit('test2',{msg: "test2"});
});
}
As you can see, the second one lacks the namespace part from the output. In the real app I'm picking the socket id from a client manager so I'm using socket.id in this piece of code instead of client.getSocketId(), but the idea is the same as I'm just echoing to the origin client here.
How do i make the second method to use the correct namespace when outputting to the client?
After checking out the source for SocketNamespace, it appears the syntax is io.of('space').socket(id).emit(....
[Edit per Fuu's comment]
To find this, I checked the Socket.IO GitHub repository and looked for a file that would have to do with namespaces--namespace.js seemed to fit the bill. The file wasn't very long, so I scanned it looking for methods on SocketNamespace's prototype that looked like it might do what we wanted.
Since you call io.sockets.socket to find a socket on the global namespace, SocketNamespace.prototype.socket stuck out to me as being promising. Furthermore, it takes a parameter called sid, and the body of the method appears to be fetching a socket from a hash of sockets by this ID. A Socket is what we want (it holds the emit method), so my presumption was that this is the method to use in this case.

Categories

Resources