New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sending to all clients except sender #15
Comments
What you pointed out is correct, each worker has its own scope and cannot access each other's variables directly as you suggested. Also, note that you should treat workers as though they are disposable and can crash at any time - This is a central idea of the 12-factor app (http://12factor.net/disposability). In SC, you can share events/data between workers using the global nData object (accessible through property of worker object): worker.global The global object is an nData client (https://github.com/topcloud/ndata) which you can use to store/retrieve data asynchronously in a central place to share between all workers. It's basically a lightweight version of Redis which runs entirely in Node.js. Note that in SocketCluster, a client will always be sent to the same worker (this applies to every HTTP request and WebSocket connection made during that session). If a user has multiple tabs open in the browser (multiple sockets), they will all be sent to the same worker as well. There are a few general rules for using variables inside workers:
All this said, when designing a distributed system, you shouldn't try to store individual clients as you are trying to do. This works against the principles of the Publish/Subscribe pattern (http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern). You should try to resist the urge of having all your workers 'know' about all of your clients (this approach is simply not scalable). Instead, you should design your system in such a way that you do not have to keep track of them at all (yes it's possible) - You will find this MUCH easier to deal with and it will scale seamlessly. Your clients should listen for events that they are interested in - In your workers, you can use the SUBSCRIBE middleware to authorize/block them from doing so. Once your client is subscribed to a channel, they will receive all events that you publish to it. You don't have to know which clients are subscribed to that channel in order to send messages to them. For example, you could have an event channel for a user ('Bob') and another user ('Alice') could send messages to that channel by emitting to it. For example, assume this client-side code (for Bob): socket.on('Bob', function (data) {
console.log('Received message from ' + data.from + ': ' + data.message);
}); Then assume this client-side code (for Alice): socket.publish('Bob', {
from: 'Alice',
message: 'Hi Bob!'
}); Here Alice is sending a message to Bob's event channel. Only clients which are subscribed to the 'Bob' event will receive the message (in this case just Bob, no one else). As mentioned before, you can control who can subscribe to a channel using middleware - You can control access by authenticating the user. See this post for more details: http://ncombo.wordpress.com/2014/08/22/full-stack-pubsub-with-socketcluster/ |
Your approach to handling events sounds good. With regard to middleware, you're almost there. next('You are not allowed to publish to this channel...') then that means fail. Also, I feel that you don't need middleware to do what you're trying to do. If you don't want a specific client socket to receive an event - All you have to do is not listen to that event. You generally don't need to block a user/socket from publishing an event to their own channel since there is no security issue with that. The publish middleware is mostly used for preventing other users from publishing events to a channel which they shouldn't have access to. Documentation on middleware is lacking at the moment, so I'll do my best to provide an explanation: Following from the example I gave earlier. To make good use of middleware, you need some way to associate sockets (or a session) with a particular user's identify. You can do this in a number of ways (you can make use of tools like Redis or other database engines to keep track of session IDs and authentication tokens). Note that you can get the session id from: socket.session.id However, probably the easiest way is to use SC's socket.session (also accessible over HTTP using req.session) object directly to set/get auth tokens, you can perform authentication over either HTTP or over a realtime connection (whatever works best for you): Example (server) code: scServer.on('connection', function (socket) {
socket.on('login', function (data) {
// Get user's actualPassword from a database then compare it with
// the provided data.password to check if the user is who they claim to be
// Here we are using MySQL (see https://github.com/felixge/node-mysql/)
connection.query('SELECT * from user WHERE username = ?', [data.username],
function (err, rows) {
var actualPassword = rows[0].password;
if (data.password == actualPassword) {
// Password matches so set a token to associate this session with the username
socket.session.set('username', data.username);
}
}
);
});
}); Then the subscribe middleware could look like this (Note that this is highly simplified): scServer.addMiddleware(scServer.MIDDLEWARE_SUBSCRIBE, function (socket, event, next) {
socket.session.get('username', function (err, username) {
if (event.indexOf(username) == 0) {
// If the event begins with the session's username token,
// then allow it to subscribe to that event
next();
} else {
// Otherwise block with appropriate error message
next("ERROR - You cannot subscribe to other users' channels");
}
});
}); |
Thanks for the tip for the next method with parameters. The middleware example provided can't be used because in my case 1 username can have multiple events. I try to achieve this: sending to all clients except sender:
|
I have an event listener:
var events = require('events');
var eventEmitter = new events.EventEmitter();
eventEmitter.on('doorOpen', function ringBell()
{
console.log('ring ring ring');
});
I have also private variables inside my workers, like:
var ClientsMap[data.userID] = {
SocketHandler: socket.id
};
The problem is when I emit an event (coming from an external request):
eventEmitter.emit('doorOpen');
I don't have access to my variable 'ClientsMap' because the variable is inside another worker.
Example: I add a user on a process PID 6910 and the event is sent to another worker 6913, so 6913 he don't have any reference to my local variable ClientsMap...
How I can send a general event between my workers (like req maybe) ?
The text was updated successfully, but these errors were encountered: