Browser connecting with multiple sockets using JS socket.io - javascript

I'm using node.js with a React front end. I'm building a GPS based MMO type of game. I recently decided to drop most HTTP requests and go with sockets, so I can emit data whenever I want and the front end only has to worry about what to do with it when it receives it.
I've built sites with sockets before, but never ran into this issue.
Basically, every time my browser opens a socket connection with node, it opens 2-3 connections at once(?). When it disconnects, I get the console.log stating that 3 socket connectionss have been closed. It'll look like this:
User disconnected
User disconnected
A user has connected to the system with id: nUMbkgX6gleq-JZQAAAD
A user has connected to the system with id: CzFtR2K5NJ1SoiHLAAAE
A user has connected to the system with id: tgGYhpXuOONmL0rMAAAF
For now, it wouldn't be an issue, however I'm only getting the FIRST 'inventory' emit to work. Later when I call the function to emit inventory again, the browser doesn't seem to get it. But the console logs in the node function will trigger correctly.
I have a feeling that this has something to do with multiple sockets opening.
Here is the React Event:
this.socket.on("inventory", charData => {
console.log('heres the data', charData)
props.setCharStats(charData[0])
})
Here's the node emitter:
const getCharInventory = (charId, userId) => {
dbInstance
.getCharInventory(charId)
.then(response => {
console.log( // this console.log happens just fine
"emmited inventory, userId is: ", userId, " with this response: ", response)
socket.emit("inventory", response)
})
.catch(err => console.error(err))
}

I'm such a dork. I was starting multiple connections within multiple connections.
In more than one component, I had this...
this.socket = socketIOClient(`${process.env.REACT_APP_proxy}`)
Sooo..... yeah. Just an FYI to others, it's better to connect in 1 file and import it into others.

Related

Ideal way to handle multiple socket rooms?

So I am creating a chat application and I want to handle multiple chat rooms. Now I watched some tutorials and came up with a way.
const io = require("socket.io")(http);
io.on("connection", (socket) => {
socket.on("joinRoom", (roomid) => {
//Joining the room
socket.join(roomid)
//Broadcasting all previous messages
io.to(roomid).emit("messages",allPreviousMessages)
})
socket.on("chatMessage", (data) => {
//Saving msg to dB then broadcasting
io.to(roomid).emit("message",receivedMessage)
})
socket.on("disconnect",(data) => {
//updating user's lastSeen info in dB
})
})
So on my frontend when user clicks on a chatroom we call the "joinRoom" event and connect to the room and on clicking another chatroom make the same process of joining room.
Is this an ideal way for handling multiple chatrooms? If not so please let me know a better solution.
I think the best way to implement private rooms or channels or chats is this way. I have implemented an example for these three sections. Link
User token and api must be authenticated before connecting to socket.io. If this part is ok it will connect to socket.io otherwise it can't cause you to see the event that is there. Something happens by calling each of them. For example, by calling this onNotificationForVoiceCall event, the received data is first checked, then it is checked whether this user is present in the list of online users or not, and the state of the next step is checked. Whether or not this room has already been created in the database, the response of all these operations is returned to the user by socket.emit,
And I fixed some bug in project.

multiple browser instances with websocket capabilities in node?

Let's say I am building a social app. I want to log into multiple accounts (one per browser instance) without an user interface (all via node), and by calling all respective endpoints to log in and start chatting.
The important part is to test when an user closed the tab or logs out or leaves the group and therefore the websocket's connection closes.
If I understand you correctly.
You would like to make a server-side event happen whenever a client connects or disconnects, without any html,css.... or any other user interface.
You can do it like this in node :
For connection you use :
Server.on("connection,function(ws){some stuff...})
The function that is called on connection will get the websocket that connected as parameter by default. I just use a lambda function here you can also call one that will then get the websocket as parameter.
For disconnection you put a function in the Server.on function to monitor when the client disconnected. Like this :
Server.on("connection,function(ws){
ws.onclose = function (ws) {
some stuff...
}
})
You can again replace the lambda function by another one.
Server is in my case equal to this :
const wsserver = require("ws").Server
const server = new wsserver({ port: someport })
But it can vary.
All you need to do apart from that is connect the client.
I do it like this but it can vary as well.
const ws = new WebSocket("ws://localhost:someport");
I hope this is helpful.

Unable to get Notify data using Noble

Can't receive any notifications sent from the Server peripheral.
I am using ESP32 as Server with the "BLE_notify" code that you can find in the Arduino app (File> Examples ESP32 BLE Arduino > BLE_notify).
With this code the ESP32 starts notifying new messages every second once a Client connects.
The client used is a Raspberry Pi with Noble node library installed on it (https://github.com/abandonware/noble). this is the code I am using.
noble.on('discover', async (peripheral) => {
console.log('found peripheral:', peripheral.advertisement);
await noble.stopScanningAsync();
await peripheral.connectAsync();
console.log("Connected")
try {
const services = await peripheral.discoverServicesAsync([SERVICE_UUID]);
const characteristics = await services[0].discoverCharacteristicsAsync([CHARACTERISTIC_UUID])
const ch = characteristics[0]
ch.on('read', function(data, isNotification) {
console.log(isNotification)
console.log('Temperature Value: ', data.readUInt8(0));
})
ch.on('data', function(data, isNotification) {
console.log(isNotification)
console.log('Temperature Value: ', data.readUInt8(0));
})
ch.notify(true, function(error) {
console.log(error)
console.log('temperature notification on');
})
} catch (e) {
// handle error
console.log("ERROR: ",e)
}
});
SERVICE_UUID and CHARACTERISTIC_UUID are obviously the UUIDs coded in the ESP32.
This code sort of works, it can find Services and Characteristics and it can successfully connect to the peripheral, but it cannot receive messages notifications.
I also tried an Android app that works as client, from that app I can get all the messages notified by the peripheral once connected to it. So there is something missing in the noBLE client side.
I think there is something wrong in the on.read/on.data/notify(true) callback methods. Maybe these are not the methods to receive notifications from Server?
I also tried the subscribe methods but still not working.
The official documentation is not clear. Anyone could get it up and running? Please help.
on.read/on.data/ are event listeners. There is nothing wrong with them. They are invoked when there is a certain event.
For example adding characteristic.read([callback(error, data)]); would have invoked the on.read.
From the source:
Emitted when:
Characteristic read has completed, result of characteristic.read(...)
Characteristic value has been updated by peripheral via notification or indication, after having been enabled with
characteristic.notify(true[, callback(error)])
I resolve using the following two envs NOBLE_MULTI_ROLE=1 and NOBLE_REPORT_ALL_HCI_EVENTS=1 (see the documentation https://github.com/abandonware/noble)

How to create sockets that particular to user and disposable on Socket.Io

I write a Node.Js app and I use Socket.Io as the data transfer system, so requests should be particular to per user. How can I make this?
My actual code;
node:
io.on('connection', (socket) => {
socket.on('loginP', data => {
console.log(data);
})
})
js:
var socket = io('',{forceNew : false});
$("#loginbutton").click(function() {
var sessionInfo = {
name : $("#login input[name='username']").val(),
pass : $("#login input[name='pass']").val()
}
socket.emit("loginP", sessionInfo)
})
It returns one more data for per request and this is a problem for me. Can I make this on Socket.Io or should I use another module, and If I should, which module?
If I understand your question correctly (It's possible I don't), you want to have just one connection from each user's browser to your nodejs program.
On the nodejs side, your io.on('connection'...) event fires with each new incoming user connection, and gives you the socket for that specific connection. So, keep track of your sockets; you'll have one socket per user.
On the browser side, you should build your code to ensure it only calls
var socket = io(path, ...);
once for each path (your path is ''). TheforceNew option is for situations where you have multiple paths from one program.

Socket IO and Dynamic Rooms

If anyone is experienced with Websockets / Socket IO hopefully you can point me in the right direction.
I made a Discord Clone and I'm trying to optimize it to scale better. Right now when a user sends a message I query the DB for all users part of that server, and emit a message to their specific socket. This is obviously not going to scale well as every message requires a expensive query and lookup in the client list
// Emit messages to only users part of specific server
// Will only return list of users part of server and active in last 10 minutes
sqlQuery = `SELECT userservers.user_id FROM userservers
JOIN users ON users.user_id = userservers.user_id AND users.user_last_active > (NOW() - INTERVAL 10 minute)
WHERE server_id = ${sql.escape(serverId)}`;
const users = await sql.query(sqlQuery);
action = { type: "message", payload: msg };
// Iterate over users, and find them in clients list
// Emit over socket only to that user
users.forEach((user) => {
clients.forEach((client) => {
if (client.userId === user.user_id) {
io.to(client.id).emit(user.user_id, action);
}
})
});
However using Rooms for each Sever would eliminate my need to query the DB. I understand I can do this when the socket server first starts
// Get server list from Mysql DB
servers.forEach((server) => {
socket.join(server.name);
}
However my issue becomes, when a user create a new server once the application is already running It will not update the list.
I am probably missing some concept on creating dynamic rooms.
EDIT : I am thinking the solution could be that every time a "server" is created, I send a message to the socket server so it can join that "room"
Right now when a user sends a message I query the DB for all users part of that server
I think you can submit broad cast message to all online users, so instead of forEach client => io.to(clientId) you can submit broad cast message to all connected users io.emit('some event', { for: 'everyone' });
also I'm wondering why you are creating many servers? you can divide your server into namespaces by using const namespace = io.of('/thisIsASeparateNamespace'); and also you can submit broadcast messages to all users inside this name space by namespace.emit('some event', { for: 'everyone in name space' });
So your chat structure can be like this
Server
Namespaces // for separate chat app / or like slack work spaces
Rooms // for group chatting
ClientID // for one to one

Categories