I'm trying to setup a simple chat app that uses WebSockets in a serverless way.
I'm using Azure's WebPubSub service and the code is hosted in an Azure Function App.
I'm using javascript and basing my code in this sample app: https://github.com/Azure/azure-webpubsub/tree/main/samples/functions/js/simplechat
That link has all the backend code and all the trigger and bindings are the same as mine.
I can run code on the following events :
connect
connected
message
disconnect
for the connect event I have the following code: (connect/index.js)
context.bindings.actions.push({
"actionName": "addUserToGroup",
"userId": `${context.bindingData.connectionContext.userId}`,
"group": "group1"
});
that basically tells webPubSub to add the user to a group, in this case called "group1".
For my application, when I send a message, from the client to the socket, I need to know if I'm the only one connected to that group.
So far I didn't find anything in the docs regarding this.
In the sample app, whenever there's a message the following code runs (message/index.js)
var response = {
....
"states": {
"counterState": {
counter: msgCounter,
}
}
};
return response;
they increment a msgCounter variable whenever there's a message and save it to the connection state
I tried incrementing a variable when the connected event fires but is seems that I cannot set the state in that specific function. I then tried to do that on the message event but it seems that the state is not global for all the connections (i.e. it is specific to a unique connection)
Any help on this would be much apreciated.
Thank you
Related
I previously asked this question but it was closed for duplication owing to this thread (SignalR - Sending a message to a specific user using (IUserIdProvider) *NEW 2.0.0*) - but this doesn't show the JavaScript as made clear in my title.
I have a WebForm application in ASP.Net that uses SignalR to push live data to the user logged in. The setup works perfectly, but realised I am broadcasting messages to all clients, which whilst it doesn't cause the wrong data to displayed to the logged in user, does cause the JavaScript function to get called for all users when just one has a data push.
I have amended the Hub code to broadcast to a specific user (User) and provided the User ID, and I have also tried Client with a Connection ID. Both fire off fine in the codebehind, but the javascript will not update the front end.
I believe it's because the JavaScript has not been modified to listen for a message sent to the user, but I'm not sure how I need to adapt the code to allow the message to be received.
The 2 tried lines in Hub here:
context.Clients.User(Me.Context.User.Identity.GetUserId()).ReceiveNotification(notifCount)
context.Clients.Client(Me.Context.ConnectionId).ReceiveNotification(notifCount)
JavaScript/jQuery function for the SignalR message here:
$(function () {
var nf = $.connection.notificationHub;
nf.client.receiveNotification = function (notifCount) {
// Update data
}
$.connection.hub.start().done(function () {
nf.server.sendNotifications();
}).fail(function (e) {
alert(e);
});
//$.connection.hub.start();
});
For calling back to the client (or self) you should use:
Clients.Caller.addContosoChatMessageToPage(name, message);
And for calling users you should use:
Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);
Reference - docs
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.
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.
This question already has an answer here:
Emitting a message in sails v0.11 (client-side)
(1 answer)
Closed 6 years ago.
The server: sails.js (0.11.x) is the server
The client: A node.js script with sails.io#0.11.5 and socket.io-client#1.3.5
Big picture: I have, or will have, a farm of node.js scripts that connect to the sails.js server and will perform various tasks.
Immediate Goal: I want to emit an event during a socket connection from client->server such as:
socket.emit('got_job', job.id);
Why? If this is possible I can create various event handlers on the server side in one controller (or controller + service) and keep my code clean while managing a set of stateful transactions between client/server endpoints for supporting this script farm.
The documentation: This is how one goes about using socket.io-client for sails.js this per sails docs: https://github.com/balderdashy/sails.io.js?files=1#for-nodejs
I haven't much code to share other than what's in that link, but I'll paste it here just in case:
var socketIOClient = require('socket.io-client');
var sailsIOClient = require('sails.io.js');
// Instantiate the socket client (`io`)
// (for now, you must explicitly pass in the socket.io client when using this library from Node.js)
var io = sailsIOClient(socketIOClient);
// Set some options:
// (you have to specify the host and port of the Sails backend when using this library from Node.js)
io.sails.url = 'http://localhost:1337';
// ...
// Send a GET request to `http://localhost:1337/hello`:
io.socket.get('/hello', function serverResponded (body, JWR) {
// body === JWR.body
console.log('Sails responded with: ', body);
console.log('with headers: ', JWR.headers);
console.log('and with status code: ', JWR.statusCode);
// When you are finished with `io.socket`, or any other sockets you connect manually,
// you should make sure and disconnect them, e.g.:
io.socket.disconnect();
// (note that there is no callback argument to the `.disconnect` method)
});
What I have looked into: I've drilled into various levels of these objects and I can't seem to find anything exposed to use. And simply trying io.socket.emit() as it doesn't exist. But io.socket.get() and io.socket.post(), etc work fine.
console.log(socketIOClient);
console.log(sailsIOClient);
console.log(io);
console.log(io.socket._raw);
console.log(io.sails);
Thanks, and I'll try to update this as needed for clarification.
UPDATE:
Misc Server Info.:
I'm using nginx on port 443, with SSL termination, pointing to 4 (and
soon more) sails.js instances on separate ports (3000-3003).
I'm also using Redis for sessions and sockets.
You're close:
Your io.socket.get call is kind of like a rest api call. You'd need a sails controller bound to a get request on the url '/hello',
//client
io.socket.get('/hello', function serverResponded (body, JWR) {
//this is the response from the server, not a socket event handler
console.dir(body);//{message:"hello"}
});
in
config/routes.js:
{
'get /hello':'MyController.hello'
}
//controllers/MyController
{
hello:function(req,res){
res.json({message:"hello"});
}
}
The method you're looking for is, io.socket.on('eventName');
Here's an example:
//client
io.socket.get('/hello', function serverResponded (body, JWR) {
//all we're doing now is subscribing to a room
console.dir(body);
});
io.socket.on('anevent',function(message){
console.dir(message);
});
in
config/routes.js:
{
'get /hello':'MyController.hello'
}
//controllers/MyController
{
hello:function(req,res){
sails.sockets.join(req.socket,'myroom');
res.json({message:'youve subscribed to a room'});
}
}
What we've effectively done is, setup our socket to be part of a "room", which is basically an event namespace. Then, some other controller only has to do this:
sails.sockets.broadcast('myroom','anevent',{message:'socket event!'});
After this call is made, you would receive the message in the callback of io.socket.on('anevent').
where is a good place to put my logic if I want to use sails.io? Is config/bootstrap.js a good place to put it? Or is there some other file I can create somewhere else?
This code below works:
// config/bootstrap.js
module.exports.bootstrap = function (cb) {
sails.io.sockets.on('connection', function(socket) {
console.log("Got a connected client");
});
cb();
};
It doesn't support this until 0.9.4.
step 1. Get the latest version of sails.js
step 2. Generate sails with the the cli
step 3. See config/sockets.js, customize onConnect function, see below:
module.exports.sockets = {
// This custom onConnect function will be run each time AFTER a new socket connects
// (To control whether a socket is allowed to connect, check out `authorization` config.)
// Keep in mind that Sails' RESTful simulation for sockets
// mixes in socket.io events for your routes and blueprints automatically.
onConnect: function(session, socket) {
// By default: do nothing
// This is a good place to subscribe a new socket to a room, inform other users that
// someone new has come online, or any other custom socket.io logic
console.log("Got a connected client");
},
...
For logic processing, you can put it in the following places:
Controller: if a request should trigger a real-time event
Service: if you want :) but I think Controller is referred
/config/socket.js onConnect(), onDisconnect(): If you want to add or remove the connected socket to/from some rooms, or some initial socket setup, etc.
/policies/sessionAuth.js: for some real-time authen logic
Other places...
Beside, you should consider the resourceful-pubsub feature which may help you save a lot of effort on implementing real-time process with socket. I found that it's very cool :)