I'm building a server using Node.js. I store the objects representing my users in an associative array and right now the index of those objects are the sockets id of that connection. Using the Socket.io library i obtain it by simply doing socket.id, so for each handler that takes care of my requests I can always know which user made the request.
Client-side every user has the ids of the connected users (that are different from the socket ids).
The problem araises when i have an handler that is used to send a message from an user to another, i make an example:
User A needs to send a message to user B, my protocol specifies the message as something like this:
MSG_KEY:{MSG:'hello world',USER_ID:'12345'}
Server-side i have an handler that listens to "MSG_KEY" and when the message is sent it is executed and using the socket.id I can retrieve who made the request, but the problem is that i need to get also the user B but this time using his USER_ID. I don't want to use the socket.id to avoid session spoofing.
I first thought about indexing in my array the users by indexing them both from socket.id and user id.
My question is: is it a good idea to do this? Does socket.io provide a way to simplify this?
There's no particular reason you can't do that. I would use two separate objects (maps), and probably have the users be represented by objects rather than strings so that there was really only one user (referenced from two places).
E.g., a user:
var user = {userid: "JohnD", name: "John Doe"};
and then:
var usersByID = {};
usersByName[user.userid];
and
var usersBySocket = {};
usersBySocket[theSocketID] = user;
Or even
var users = {
byID: {},
bySocket: {}
};
users.byID[user.userid] = user;
users.bySocket[theSocketID] = user;
Related
i have a Problem!
When two users playing a game and they click very near at the same time, all two users get what only the 2nd user must get.
Is it possible to make an array by his userid or something?
I also have for anti spam like that
anti_spam[socket.request.session.passport.user.id]
But thats a json
If i try the same or array, i get error SyntaxError: Unexpected token [
How can i make sure, that each user get only his own items and not items from another user when opening at the same time?
I use sockets, here is a unique userid socket.request.session.passport.user.id
This is the array
var WinItem = [];
And if two users start like milliseconds same time, than the second overwrite the first...
! CODE NOT TESTED!
Wait wait wait wait...
So I only know ws (Node.js) but there you first get a onConnection event.
In that function you add a UID to the connection element. Then if you receive a message from the client you should have you're connection object and therefore an UID. You now can store the won item in the connection object (if you want) maybe like so:
ws.onConnection = con => {
con.UID = generateUID();
con.inventory = [];
con.onMessage = msg => {
[..STUFF/CODE/YAY...]
con.inventory.push(item);
con.send("You've won wooohoo");
});
});
Did you mean something like this??
Otherwise please be more specific about what you want.
(You can also store the stuff somewhere else together with the UID but that would add quiet some code)
Is there a way to store all emits of socket.io in a global array so that I can loop through the emits when a new user joins the website and can pickup where the 'canvas drawing' currently is. I want the new user to see what work has already been done and then collaborate on it.
Any other ways I could approach towards this?
If you just want the emits stored for the duration of the server running, you can simply declare a module level array and push each emit into that array. Then, at any future time during that server execution, you can consult the array.
If, you want the emits stored across server invocations, then you would need to store them to some persistent storage (file, database, etc...).
// save incoming data from one particular message
var emitsForSomeMessage = [];
io.on("someMessage", function(data) {
// save the incoming data for future reference
emitsForSomeMessage.push(data);
});
Or, if you're trying to store all outgoing .emit() data, then you can override that method and save away what is sent.
var outgoingEmits = [];
(function() {
var oldEmit = io.emit;
io.emit = function(msg, data) {
outgoingEmits.push({msg: msg, data: data});
return oldEmit.apply(this, arguments);
};
})();
Since there are many different messages that may be sent or received, you can add your own logic to decide which messages are saved in the array or not.
I keep track the list of every users connected in the array.
So if there is a new connection, it will check whether the user is already on the list or not, if he was already on the list, then assign their socket.id with the corresponding socket.id on the list, otherwise just add them to the list.
It's for preventing same user counted as 2 user while he attempt to do multi-login.
Object.keys(client).forEach(function (key) {
if (client[key].id == data.id){
is_connected = true;
socket.id = key;
}
});
I have no problem handling the messages/chat that was sent/received by the user who attempt multi-login.
socket.on('chat', function(msg){
var data = {"name": client[socket.id].name, "message": msg};
io.emit('chat', data);
});
The io.emit for the chat message was succesfully sent to the user who attempting multi-login.
The problem I got was whenever the user decide to logout/disconnect from the server.
io.emit('user_leave', client[socket.id].id);
[Multi-Login Case] -> Multi-User and Dual-User are same user attempting Multi-Login
Whenever the Main-User disconnected from the server, the Dual-User received 'user_leave' sent by the server, because io.emit supposed to send it to all sockets.
But not otherwise, while the Sub-User disconnected from the server, the Main-user do not receive 'user_leave' emitted by the server.
*Note: Main-User is login first, then the Dual-User. So the Main-User information was saved directly in the array, while the Sub-User socket.id was assigned with the Main-User socket.id
[Update]
B2 socket.id was assigned with B1 socket.id, the io.emit for chat work perfectly while io.emit for disconnect only emitted to All except Dual-User(B2)
socket.id is used internally by socket.io for its own socket list. You cannot overwrite that or you break some of its ability to maintain its own data structures.
You have two choices:
You can use the existing socket.id value as is (without overwriting it) so you don't break existing behavior. It is already guaranteed to be unique on the server.
You can use a different property name for your own id such as socket.userId and then you won't conflict.
If you need to, you can maintain a map between your own custom id and the socket.io socket.id so you could get to one from the other.
Similar question here: Socket.io custom client ID
generateId prop of io.engine object can be used for to set the custom id.
Using this way, the all socket ids can be created on the server side without any issue.
Actually I wrote an answer for a similar question today.
An example:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.engine.generateId = function (req) {
// generate a new custom id here
return 1
}
io.on('connection', function (socket) {
// listing the default namespace rooms
console.log("rooms: ", io.nsps["/"].adapter.rooms);
})
The console output would be as the following:
rooms: { '/#1': Room { sockets: { '/#1': true }, length: 1 } }
It seems to be it has been handled.
It must be in mind that socket id must be unpredictable and unique value with considering security and the app operations!
Extra: If socket.id is returned as undefined because of your intense processes on your generateId method, async/await combination can be used to overcome this issue on node.js version 7.6.0 and later. handshake method of node_modules/engine.io/lib/server.js file should be changed as following:
former:
Server.prototype.handshake = function (transportName, req) {
var id = this.generateId(req);
...
}
new:
Server.prototype.handshake = async function (transportName, req) {
var id = await this.generateId(req);
...
}
So I want to use Gravatar avatars on my website. I got the appropriate packages for it. The way it works, it turns email addresses into an "MD5Hash." That's sent to Gravatar in exchange for the image url.
Fine, but I want to display avatars without exposing everyone's email address. At the same time, I have users already that likely already have gravatars, and I think it would be cool if their avatars just popped up one day, without adding another field to the user profiles collection, or asking them to.
Is there a way to do some of this on the server and accomplish my goal?
Handlebars.registerHelper("gravatar", function(id){
var email = Meteor.users.findOne({_id: id}).emails[0].address;
var options = {
secure: true,
size: 29,
default: 'retro'
};
var md5Hash = Gravatar.hash(email);
// 5658ffccee7f0ebfda2b226238b1eb6e
var url = Gravatar.imageUrl(md5Hash, options);
// https://secure.gravatar.com/avatar/5658ffccee7f0ebfda2b226238b1eb6e
return url;
});
Hackish:
On the server:
userArray = Meteor.users.find(query,{fields: {"emails.address": 1}}).fetch();
userArray.forEach(function(el,i,a){
a[i] = { _id: el._id, md5hash: Gravatar.hash(el.emails[0].address) };
}
where query is whatever your criteria are, will get you an array of objects whose _id matches the _id of each user and whose md5hash value is the hash of that user's email. You can set up a method to return this array to you when you need it.
The good news is that your client can use these hashes to get avatars in whatever sizes might be necessary at any time.
Much less hackish:
The problem with the above is that your server is frequently going to be recomputing the md5hash of each email. Plus you're getting a potentially big and non-reactive array from the server. You'll live to regret this. You really just want to add an md5hash key to the emails array in the user document, initialize it for existing users, and make sure that new users always have this key set at creation time. This will let you handle either single-email address users or multiple-email address users.
I am developing an app with Mongo, Node.JS and Angular
Every time the object is delivered and handled in the front-end, all objectId's are converted to strings (this happens automatically when I send it as json), but when when I save objects back into mongo, I need to convert _id and any other manual references to other collections back to ObjectID objects. If I want to nicely separate database layer from the rest of my backend, it becomes even more messy, lets assume my database layer has the following signature:
database.getItem(itemId, callback)
I want my backend business treat itemId as opaque type (i.e no require'ing mongo or knowing anything about ObjectId outside of this database layer), yet at the same time I want to be able to take the result of this function and send it directly to
the frontend with express js.
exports.getItem = function(req, res) {
database.getItem(req.params.id, function(err, item) {
res.json(item);
});
};
What I end up doing now is:
exports.getItem = function(itemId, callback) {
if (typeof itemId == 'string') {
itemId = new ObjectID(itemId);
}
var query = {_id: itemId};
items.findOne(query, callback);
};
This way it can handle both calls which come from within the backend, where itemId reference might be coming from another object and thus might already be in the right binary format, as well as requests with string itemId's.
As I already mentioned above, when I am saving an object that came from front-end and which contains many manual references to other collections that is even more painful, since I need to go over the object and change all id strings to ObjectIds.
This all feels very wrong, there must be a better way to do it. What is it?
Thanks!