How synchronise socketIO connection ID's on client and server? - javascript

I have a javascript GameClient that uses SocketIO to send messages to a nodeJs server. Multiple users can open the GameClient separately and send messages to the server.
GameClient
GameClient ---> NodeJS Server
GameClient
The server can send messages to specific clients using io.to(socketid).emit(). The code looks something like this:
CLIENT
this.socket = io({ timeout: 60000 })
this.socket.on('connect', () => Settings.getInstance().socketid = this.socket.id)
this.socket.on('reconnect', (attemptNumber:number) => console.log("reconnecting..."))
const json = JSON.Stringify({socketid:this.socket.id, name:"Old Billy Bob"})
this.socket.emit('user created', json)
SERVER (simplified for clarity, just keeping track of one user here)
user = {}
io.on('connection', (socket) => {
console.log('new connection')
socket.on('disconnect', () => {
console.log('user disconnected')
});
socket.on('user created', (json) => {
user = JSON.parse(json)
});
});
// demo code, send a message to our user
io.to(user.socketid).emit("message to one user")
PROBLEM
When the client browser tab becomes inactive for any reason at all, the client disconnects and reconnects and gets a new socket connection ID. This actually happens a lot in Chrome and Safari.
The server only knows the old connection id, so now it can't send direct messages any more. How do I keep the socket connection id synchronised on the client and server?
Since the server also gets a reconnected event, how does it know which user reconnected?

The answer to your question is quite simple: you need a way to identify who is who. And that is not socket.id because this only identifies sockets, not users, as you've already noticed.
So you need some authentication mechanism. Once a user authenticates you can reuse his true id (whether it is simply a name or an integer in a database is irrelevant). And then on the server side you keep a collection of pairs (true_id, socket_id). And whenever a message comes to that user, you broadcast it to all matched socket.io objects.
Edit: So here's the flow:
Client authenticates with the server, the server sends him his own true_id, which the client stores somewhere. The client may also store some session_id or maybe some other mechanism that will allow him fast reauthentication in case of disconnection (note: do not store credentials, its a security issue).
The server keeps track of (true_id, socket_id) pairs in the form of a double way, mutlivalue map (it's an implementation detail what kind of data structure should be used here, maybe two {} objects is enough). If a connection dies then (true_id, socket_id) entry is removed. Note that for a given true_id there still may be some other socket_id alive. And so it doesn't mean that the user disconnected. It only means that this particular channel is dead.
Users don't care about socket_id, they only care about true_id. What you emit is {target_id: true_id, ...} instead of {target_id: socket_id, ...} on the client side, when you want to send a direct message.
When the server receives such message with true_id inside, it retrieves all (true_id, socket_id) pairs and passes the message to all of these sockets (note: maybe you don't even need socket_id, you can simply store socket objects here). Although this is a business logic: do you allow multiple connections per user? I would. There are many edge cases here (like for example a client thinks that he disconnected, but the server thinks he is still connected, etc) and making this 100% correct is unfortunately impossible (due to the nature of networking). But with a bit of effort it is possible to make it work 99% of the time.
If a connection dies then it is your client's responsibility to automatically reconnect and reauthenticate. New socket_id for old true_id is generated on the server side.
Let me emphasize this again: clients don't care about socket_id at all. Because that doesn't identify them. This only identifies a channel. And only the server cares about this information.

Related

Socket io Client in ReactJS is getting multiple emits from the server

I am building a chat room with some extra features, and I have a socket io server, as well as a socket io client in ReactJS.
I have it so if someone pins a message or changes the settings of the chat room, it emits the changes to the server, the server saves those changes, and then emits them back out to everyone so everyone is synced.
The settings and pinned messages successfully transfer and are communicated, I have console.logs at almost every step of the transfer, the client logs out a single request, the server logs that it emits a single time, but the client logs that it recieved an emit multiple times, sometimes a couple of times like 2-6 requests, sometime it gives out 60 requests. I'm trying to really control the efficiency, but I have no idea what is causing this
I'm not sure it matters but another thing of note is that the client also connects to a native WebSocket server to get messages from another source
what the client generally looks like is:
effect(()=>{
socket.emit('changeSetting', setting)
},[setting])
socket.on('recieveSetting', (arg)=>{
if(arg != setting){
setSetting(arg);
}
})
the server then looks like this:
socket.on('changeSetting', (arg)=>{
storedSetting = arg
socket.emit('recieveSetting', storedSetting)
})
That's the general structure, so I don't think its an issue of the code, more like if reactJS or connecting to the other websocket causes it to get multiple emits

io.emit() not emmiting data to all clients

I am creating a chat application.
In the server, we had an event.
io.emit('userAgentConnectMsg', data);
In the client, we are listening to this event.
socket.on('userAgentConnectMsg', (data) => { console.log(data)} );
Suppose think I have two clients connected to server.
If one client gets message another client will not get that.
This is happening randomly.
I changed code from io.emit to io.sockets.emit still the same issue.
And the worst thing is I am not getting any error either from client or server.
And I am not at all sure where the issue is happening from either it is from server or client.
I did some analysis for socket io before the io.emit
I logged io.sockets.adapter.sids which basically return all connected socket ids.
the issue is sometimes it shows only some sockets and sometimes it shows empty object.
Some can help me if you have faced the same issue.

MQTT PUBACK web sockets

I'm working on HiveMQ Websocket Client and I'm facing some issues with the message delivery.
so, I've come across the word PUBACK
let me explain you about my understanding and then I will ask my question.
whenever we send a message with QOS1, the hivemq server will acknowledge the sender with a PUBACK callback.
Now, I'm planning to subscibe to onPubackReceived event in my websockets, but the event is not firing after sending the message.
My Code:
var clientId = ClientIdentifier;
mqtt = new Messaging.Client(
host,
port,
clientId);
var options = {
timeout: 3,
keepAliveInterval: 60,
useSSL: useTLS,
cleanSession: cleansession,
onSuccess: onConnect,
onFailure: function (message) {
connected = false;
setTimeout(MQTTconnect, reconnectTimeout);
}
};
mqtt.onConnectionLost = onConnectionLost;
mqtt.onMessageArrived = onMessageArrived;
mqtt.onPubackReceived = OnPubackReceived;
Both the onConnectionLost and onMessageArrived are firing properly when a connection lost and message arrived, but the onPubackReceived is not firing.
please let me know, if I have understood it correctly or if I'm doing some mistake?
This not a HiveMQ issue.
My assumption is, that you used the HiveMQ Websocket Client as a starting point for your implementation.
In any case a Paho MQTT Client does not have a onPubackReceived field.
If you provide more details about your use case or what's your issue with message delivery, I might be able to point you into the right direction.
EDIT:
What you are describing is called Quality of Service 1 in MQTT. It is a guarantee, that a message is received at least once.
It is the client implementation's job to keep this guarantees and therefor resend a message, should a PUBACK not be received. Manually interfering with this behaviour in your application would result in inconsistency regarding the client's persistence.
For clarification:
Simply setting duplicate=truewill not result in a message being recognised as a duplicate. It will also have to have the the same messageID as the original.
I was not able to actually find any documentation about paho.jskeeping the Quality of Service = 1.
However, MQTT.js does.
QoS 1 : received at least once : The packet is sent and stored as long as the client has not received a confirmation from the server. MQTT ensures that it will be received, but there can be duplicates.
To sum things up:
Resending of messages, no PUBACK was received on, is the client Object's job. This is part of the MQTT specification.
Using the MQTT.js works over Websockets and ensures to keep QoS levels
Hope this helps.

socket connection in node js

I have just started working on node js.I have been trying to make chat application using node js. In which a single user can logged in through multiple devices or browsers. If I am not wrong and as I understand each of the browser communicates with different port address since socket connection is defined by IP address and port address hence when the same user logs in from two different browsers server creates two different socket Id.Now I verify if the user is already connected by checking the parameter send to socket info.Then if the user is already connected or the socket is already set for the user then I create connection to change the socket id to previous socket id as .
io.on('connection', function(socket){
socket.id = k;
});
where k is the socket id of previously connected same user
Now when any other client emits message to the current user then Is the socket id is replaced and only one browser gets message or both of them gets message. Is the connection of server is set for both browser or a single browser. Please help me for this. I am still not clear about how socket connection establishes between client and server.
Please improve if I am doing any supposition wrongly Or how do I solve the following scenerio.
Thanks in advance
If I understand your problem correctly, let me try explain in my way. Lets say you have the following code for the server:
var io = require('socket.io')(somePort); //same port for the client to connect, e.g. 3000
io.on('connection', function(socket){
// in here you should define any actions on your primary socket
// for example:
socket.on("hello", function(dataUserSendsWithTopicHello){// overly long variable name to show my point! :)
// do something with hello data
});
// this will go to anyone who is connected and has a handler like above for "hello"
socket.emit("hello", "hello to all who listen to hello topic");
});
A corresponding client would be:
var socket = io.connect('ip:port'); // e.g. localhost:3000 - same ip and port as above
socket.on('hello', function(serverResponseToHello){
// do something when server sends back something on hello
});
// send a hello to the server
socket.emit("hello", "Sending hello to server");
Now if you want to reply to one user only, or a group of people to see some message all together, like in a chat, you could leverage socket.io's room/ namespace implementation (http://socket.io/docs/rooms-and-namespaces/) and create one room for each client, or one room for a group of clients. For this, take a look at this answer: Using Socket.io with multiple clients connecting to same server
To sum it up, there is one ip and one port. Everyone listens to this ip and port and if you open three tabs and you have some implementation like the above, all tabs will be listening to "hello". Using rooms you can group your users and isolate communication so that only the users that the communication is done between the correct users and not all.
Good luck!
I too am fairly new to Sockets (and JS), and I'm trying to make a chat app too but a little differently.
As far as i understand the message will be received only on one browser. A better design would be to let the user connect through multiple browsers/devices. You can maintain a list of all the sockets a user has connected from (if you need it).
If you need a one-to-one chat, maybe you can write some code wherein all sockets the two users connect from are joined into a single room.
socket.room = roomname
socket.join(roomname)
Then you just broadcast the message to that room. That way every sockets both users have connected to will get the message.
socket.to(socket.room).broadcast.emit("message", message);
(I am saving the room info in socket, no idea if that is a good design)
Hope this helps!

Private messaging through node.js

I'm making a multiplayer (2 player) browser game in JavaScript. Every move a player makes will be sent to a server and validated before being transmitted to the opponent. Since WebSockets isn't ready for prime time yet, I'm looking at long polling as a method of transmitting the data and node.js looks quite interesting! I've gone through some example code (chat examples, standard long polling examples and suchlike) but all the examples I've seen seem to broadcast everything to every client, something I'm hoping to avoid. For general server messages this is fine but I want two players to be able to square off in a lobby or so and go into "private messaging" mode.
So I'm wondering if there's a way to implement private messaging between two clients using nodejs as a validating bridge? Something like this:
ClientA->nodejs: REQUEST
nodejs: VALIDATE REQUEST
nodejs->ClientA: VALID
nodejs->ClientB: VALID REQUEST FROM ClientA
You need some way to keep track of which clients are in a lobby together. You can do this with a simple global array like so process.lobby[1] = Array(ClientASocket, ClientBSocket) or something similar (possibly with some additional data, like nicknames and such), where the ClientXSocket is the socket object of each client that connects.
Now you can hook the lobby id (1 in this case) onto each client's socket object. A sort of session variable (without the hassle of session ids) if you will.
// i just made a hashtable to put all the data in,
// so that we don't clutter up the socket object too much.
socket.sessionData['lobby'] = 1;
What this allows you to do also, is add an event hook in the socket object, so that when the client disconnects, the socket can remove itself from the lobby array immediately, and message the remaining clients that this client has disconnected.
// see link in paragraph above for removeByValue
socket.on('close', function(err) {
process.lobby[socket.sessionData['lobby']].removeByValue(socket);
// then notify lobby that this client has disconnected.
});
I've used socket in place of the net.Stream or request.connection or whatever the thing is.
Remember in HTTP if you don't have keep-alive connections, this will make the TCP connection close, and so of course make the client unable to remain within a lobby. If you're using a plain TCP connection without HTTP on top (say within a Flash application or WebSockets), then you should be able to keep it open without having to worry about keep-alive. There are other ways to solve this problem than what I've shown here, but I hope I got you started at least. The key is keeping a persistent object for each client.
Disclaimer: I'm not a Node.js expert (I haven't even gotten around to installing it yet) but I have been reading up on it and I'm very familiar with browser js, so I'm hoping this is helpful somehow.

Categories

Resources