Sending message to a specific connected users using webSocket?
This is not only the right way to go, but the only way. Basically each connection needs a unique ID. Otherwise you won't be able to identify them, it's as simple as that.
Now how you will represent it it's a different thing. Making an object with id
and connection
properties is a good way to do that ( I would definitely go for it ). You could also attach the id
directly to connection object.
Also remember that if you want communication between users, then you have to send target user's ID as well, i.e. when user A wants to send a message to user B, then obviously A has to know the ID of B.
How to send message between users with same token using websocket?
I was not able to reproduce the issue as described, but was only able to receive the sent message to only one of the connected clients with the same token
.
The issue with the single connected client is due to webSockets[userToken] = ws
being referenced, as opposed to using an array of clients webSockets[userToken] = [];
and webSockets[userToken].push(ws);
Also be sure that you do not have a zombie process of node server.js
running in the background.
//...
const clients = {};
wss.on('connection', function connection(ws, req) {
let id = req.url.substring(req.url.lastIndexOf('/') + 1);
if ('' === id) {
//connecting id is missing
ws.terminate();
return;
}
if ('undefined' === typeof clients[id]) {
clients[id] = [];
}
console.log('Connection Received from IP: ' + req.socket.remoteAddress + ' with id ' + id);
//append websocket client to list of clients
clients[id].push(ws);
ws.on('message', function incoming(message) {
if ('' === message) {
//skip if message is blank
return;
}
try {
//process message as JSON object as "{"message": "string", "token": "string"}"
message = JSON.parse(message);
} catch(e) {
return;
}
if (!message.hasOwnProperty('token') || '' === message.token) {
//token was not sent or is empty
return;
}
let token = message.token;
console.log('Message received for token ' + token);
if ('undefined' !== typeof clients[token] && clients[token]) {
clients[token].forEach(function(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
//do not send message back to the sending client
client.send(message.message + ' ' + token);
}
});
}
//inform sending client message was sent
ws.send('Sent: ' + message.message);
});
ws.on('close', function() {
clients[id].forEach(function(client, index) {
if (client === ws) {
//remove the client from the pool
clients[id].splice(index, 1);
}
});
});
});
html code
<button type="button" id="sendBtn" class="btn-primary">Send</button>
<script type="text/javascript">
const socket = new WebSocket('ws://localhost:8080/{{ token }}');
socket.onmessage = function(evt) {
window.console.log(evt.data);
};
jQuery(function($) {
$('#sendBtn').on('click', function() {
const message = {
"message": "Hello World",
"token": "{{ token }}"
};
socket.send(JSON.stringify(message));
});
});
</script>
websocket send to specific user nodejs
OK. Still trying to understand the actual spec you're shooting for. But, assuming the following (based on your answers to my prior questions):
- A client connects using a webSocket.
- When they send a message over that webSocket, that message is an
id
of something that can be looked up in your database and that they want regular updates for. - Those updates for that particular
id
should be sent only to that specific client that requested it. - If a different client connects and specifies some
id
, they should get updates for thatid
only. - When a client sends a new message that specifies a different
id
, their updates should now be only for that newid
. - Updates for the
id
that one client requested are sent only to that one client (not to the other clients).
If that's what you really want, here's a way to structure that.
const wss = new WebSocket.Server({port: 10001});
// make database connection that all users share
client.connect();
wss.on('connection', function connection(ws) {
// these variables are unique to each ws connection
let interval, query;
// when webSocket closes, stop any current interval timer associated with this webSocket
ws.on('close', function() {
if (interval) {
clearInterval(interval);
}
});
// when we get an id, start querying for updates on that id
ws.on('message', function incoming(id) {
console.log(`received: ${id}`);
query = {
text: "SELECT id,state,users_list_name,user_win,timer_stamp FROM products WHERE id = " + parseInt(id) + " AND circle = 1 ORDER BY CASE WHEN state = \'available\' THEN \'1\' WHEN state = \'soon\' THEN \'2\' WHEN state = \'expired\' THEN \'3\' END",
};
// if interval is not already going, start it
if (!interval) {
interval = setInterval(function() {
client.query(query, (err, res) => {
if (err) {
console.log(err);
console.log(query);
} else {
// send data to just the one client that this timer is for
ws.send(JSON.stringify(res.rows));
}
});
}, 300);
}
});
});
Now, some comments:
Polling the database on a short time interval with a separate polling loop for every single client simply will not scale at all. You will have serious database scale issues. You really need a better design here, but since we don't know the overall requirements and architecture of your application, we don't have enough info to know what to suggest. Probably you want to leverage notifications in a database that tell you when data has changed rather than you polling it on a short interval on behalf of every single client.
I could find no reason for the
lookup
data structure. Your comments say that you want to send updates to ONE specific client, the one that requested that id. That can be done withws.send()
.This code assumes that the
client
variable represents a connection to your database that each of the setIntervals for each connected client can all share. That's why that code was moved out of thewss.on('connection', ...)
event handler.I switched to the more common terminology of
wss
to refer to the server instance andws
to refer to the webSocket for a particular connected client.ws.send()
is how you send to a connected client. I still don't know what you were doing withop.id
. Looking at the doc for thews
library, that doesn't appear to be something you can use to send to.Your code (and this code) creates a separate
setInterval()
timer for every webSocket client that connects and it uses a very short interval time. This will not scale. At worst, the interval time needs to be lengthened into multiple seconds (depending upon desired target scale). At best, you need to stop polling the database entirely and use some other mechanism in the database for getting notifications when data has been changed.
Sending messages to specific user with Spring Websocket
Can you see that the client subscribes successfully to your endpoint?
I think you are missing the first /
in the client code 'user/queue/greetings'
should be '/user/queue/greetings'
Related Topics
How to Play HTML5 Video in Reverse
Make Checkbox Behave Like Radio Buttons with JavaScript
JavaScript on the Bottom of the Page
Trigger Autocomplete Without Submitting a Form
Screen Scraping from a Web Page with a Lot of JavaScript
Bootstrap Dropdown Closing When Clicked
Clearing My Form Inputs After Submission
Saving and Loading an Image from Localstorage
Possible to Associate Label with Checkbox Without Using "For=Id"
Img-Src' Was Not Explicitly Set, So 'Default-Src' Is Used as a Fallback
How to Disable an Input Type=Text
Inlining Ecmascript Modules in HTML
JavaScript Syntax (0, Fn)(Args)
Write/Add Data in JSON File Using Node.Js