I am trying to emit to a particular socket ID:
socket(user[playID]).emit('correct', data);
But I'm getting:
TypeError: object is not a function
if I log out user[playID] I do get a valid socket ID.
Appreciated!
Here is my setup in case I'm missing something:.
// Tell Socket.io to pay attention
servio = io.listen(server);
// Tell HTTP Server to begin listening for connections on port 3250
sock = server.listen(3250);
This should do it
servio.sockets.socket(id).emit('hello');
This answer covers the same/similar topic. In short, consider keeping a reference to the connected clients yourself and emit to them as desired, rather than relying on socket.io's internals, which could change.
Per http://socket.io/docs/rooms-and-namespaces/,
Each Socket in Socket.IO is identified by a random, unguessable,
unique identifier Socket#id. For your convenience, each socket
automatically joins a room identified by this id.
You emit to a room like:
io.to('some room').emit('some event')
If you want to emit to just a specific socket.id, just replace 'some room' with the associated id.
Another way to do this is:
var players = [];
io.sockets.on('connection', function(socket){
socket.on('skt_init', function (data) {
var player = new Object();
player.id = data.id;
player.socket = socket.id;
players.push(player);
});
socket.on('disconnect', function() {
var len = 0;
for(var i=0, len=players.length; i<len; ++i ) {
var p = players[i];
if(p.socket == socket.id){
players.splice(i,1);
break;
}
}
});
socket.on('skt_event', function(data, id_player){
var len = 0;
for(var i=0, len=players.length; i<len; ++i ) {
var p = players[i];
if(p.id == id_player){
io.sockets.socket(p.socket).emit('correct', data);
break;
}
}
});
Hope that helps somewhat.
UPDATE: in socket.io-1.4 you have to prepend "/#" to socket id ( very frustrating that it doesnt work now). you can also view all connected sockets from backend as io.sockets.connected
io.to( "/#" + socket_id).emit("event_name",{data:true})
This should work:
servio.sockets.sockets[playId].emit(...)
Since socket.io doesn't provide a stable API to get the socket from a socket's ID, you can simply (and easily) keep track of them with an object keyed by socket IDs.
sockets_by_id = {}
io.on "connection", (socket)->
sockets_by_id[socket.id] = socket
sockets_by_id[socket_id].emit event, data...
(^CoffeeScript^)
Related
I would like to get a multi-process node. Workers are listening clients connections. I need pass sockets to master process because master process emit message to clients. Workers also need socket to emit message to clients.
Socket is a circular object and can't pass to a master process.
My code:
const cluster = require('cluster');
const http = require('http');
var io = require('socket.io');
var users;
var clients = {};
if (cluster.isMaster) {
function messageHandler(msg) {
if (msg.usersarray) {
usersarray = msg.usersarray;
console.log(usersarray);
}else if(msg.socket){
clients[usersarray["manu"][0]] = msg.socket;
clients[usersarray["manu"][0]].emit("hola","hola");
}
}
// Start workers and listen for messages containing notifyRequest
const numCPUs = require('os').cpus().length;
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
Object.keys(cluster.workers).forEach((id) => {
cluster.workers[id].on('message', messageHandler);
});
}else {
// Create server & socket
var server = http.createServer(function(req, res){
// Send HTML headers and message
res.writeHead(404, {'Content-Type': 'text/html'});
res.end('<h1>Aw, snap! 404</h1>');
});
server.listen(3000);
io = io.listen(server);
// Add a connect listener
io.sockets.on('connection', function(socket) {
var hs = socket.handshake;
console.log("socket connected");
if(users == undefined){
users = {};
}
if(hs.query.usuario != undefined){
if(users[hs.query.usuario] == undefined){
users[hs.query.usuario] = new Array();
}
users[hs.query.usuario].push(socket.id); // connected user with its socket.id
clients[socket.id] = socket; // add the client data to the hash
process.send({ usersarray: users});
process.send({ socket: socket});
}
// Disconnect listener
socket.on('disconnect', function() {
console.log('Client disconnected.');
});
});
}
in line process.send({ socket: socket}); Node js get error "TypeError: Converting circular structure to JSON"
-I used some module to transform circular object but don't working.
-I tried to pass socket id and then in master process, created new socket with this id but I didn't know to use it.
There is any posibility to pass socket from worker to master process?
Node js version: v5.5.0
Hm, I don't think it is possible what you are trying to do. When you create a cluster it means that you create separate processes (master + workers) which can only talk over the pipe.
Talking over the pipe means they can only send strings to each other. process.send tries to serialize a Javascript object as JSON (--> making a string out of it) using JSON.stringify. JSON for example cannot have functions, circles, etc. I just checked the socket object, it is very complex and contains functions (such as socket.emit()), so you cannot just serialize it and send it over the pipe.
Maybe you can check this or this on how to use clustered WebSockets.
It doesn't seem very trivial.. Maybe you can just pass CPU intensive tasks to some worker processes (via cluster or just spawning them yourself), send the results back to the master and let him do all the communication with the client?
I understand your purpose of broadcasting to all the node worker processes in a cluster, although you can not send socket component as such but there is a workaround for the purpose to be served. I will try an explain with an example :
Step 1: When a client action requires a broadcast :
Child.js (Process that has been forked) :
socket.on("BROADCAST_TO_ALL_WORKERS", function (data)
{
process.send({cmd : 'BROADCAST_TO_ALL_WORKERS', message :data.message});
})
Step 2: On the cluster creation side
Server.js (Place where cluster forking happens):
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();
worker.on('message', function (data) {
if (data.cmd === "BROADCAST_TO_ALL_WORKERS") {
console.log(server_debug_prefix() + "Server Broadcast To All, Message : " + data.message + " , Reload : " + data.reload + " Player Id : " + data.player_id);
Object.keys(cluster.workers).forEach(function(id) {
cluster.workers[id].send({cmd : "BROADCAST_TO_WORKER", message : data.message});
});
}
});
}
cluster.on('exit', function (worker, code, signal) {
var newWorker = cluster.fork();
newWorker.on('message', function (data) {
console.log(data);
if (data.cmd === "BROADCAST_TO_ALL_WORKERS") {
console.log(data.cmd,data);
Object.keys(cluster.workers).forEach(function(id) {
cluster.workers[id].send({cmd : "BROADCAST_TO_WORKER", message : data.message});
});
}
});
});
}
else {
//Node Js App Entry
require("./Child.js");
}
Step 3: To Broadcast in the child process
-> Put this before io.on("connection") in Child.js
process.on("message", function(data){
if(data.cmd === "BROADCAST_TO_WORKER"){
io.sockets.emit("SERVER_MESSAGE", { message: data.message, reload: data.reload, player_id : data.player_id });
}
});
I hope its clear. Please comment if its confusing ... I will try and make it clear.
Is it possible to make socket.io broadcast to all users of a namespace who are in both room A and room B but not those who are just in room A or room B?
If not, how would I go about implementing this myself? Is there a way to retrieve all users in a namespace who are in a given room?
I am working with socket.io 1.0 in node
Edit:
If there is no native method, how would I go about to create my own syntax such as:
socket.broadcast.in('room1').in('room2').emit(...)?
You can look up all the users of a room using (ref How to update socket object for all clients in room? (socket.io) )
var clients = io.sockets.adapter.room["Room Name"]
So given two arrays for your 2 rooms' roster list, you can compute the intersection using something like the answer here (ref: Simplest code for array intersection in javascript)
And finally you can take that list of users in both rooms and emit events using (ref: How to update socket object for all clients in room? (socket.io) )
//this is the socket of each client in the room.
var clientSocket = io.sockets.connected[clientId];
//you can do whatever you need with this
clientSocket.emit('new event', "Updates");
The alternate ofcourse is to have hidden rooms, where you maintain a combination of all rooms, and add users to those rooms behind the scenes, and then you are able to just simply emit to those hidden rooms. But that suffers from an exponential growth problem.
There is no in-built way to do this. So first let's look up how the broadcast works:
https://github.com/Automattic/socket.io/blob/master/lib/namespace.js 206...221-224...230
this.adapter.broadcast(packet, {
rooms: this.rooms,
flags: this.flags
});
Now we know every broadcast creates a bunch of temp objects, indexOf lookups, arguments slices... And then calls the broadcast method of the adapter. Lets take a look at that one:
https://github.com/Automattic/socket.io-adapter/blob/master/index.js 111-151
Now we are creating even more temp objects and loop through all clients in the rooms or all clients if no room was selected. The loop happens in the encode callback. That method can be found here:
https://github.com/socketio/socket.io-parser/blob/master/index.js
But what if we are not sending our packets via broadcast but to each client separately after looping through the rooms and finding clients that exist both in room A and room B?
socket.emit is defined here: https://github.com/Automattic/socket.io/blob/master/lib/socket.js
Which brings us to the packetmethod of the client.js:
https://github.com/Automattic/socket.io/blob/master/lib/client.js
Each directly emitted packet will be separately encoded, which again, is expensive. Because we are sending the exact same packet to all users.
To answer your question:
Either change the socket.io adapter class and modify the broadcast method, add your own methods to the prototype or roll your own adapter by inheriting from the adapter class). (var io = require('socket.io')(server, { adapter: yourCustomAdapter });)
Or overwrite the joinand leave methods of the socket.js. Which is rather convenient considering that those methods are not called very often and you don't have the hassle of editing through multiple files.
Socket.prototype.join = (function() {
// the original join method
var oldJoin = Socket.prototype.join;
return function(room, fn) {
// join the room as usual
oldJoin.call(this, room, fn);
// if we join A and are alreadymember of B, we can join C
if(room === "A" && ~this.rooms.indexOf("B")) {
this.join("C");
} else if(room === "B" && ~this.rooms.indexOf("A")) {
this.join("C");
}
};
})();
Socket.prototype.leave = (function() {
// the original leave method
var oldLeave = Socket.prototype.leave;
return function(room, fn) {
// leave the room as usual
oldLeave.call(this, room, fn);
if(room === "A" || room === "B") {
this.leave("C");
}
};
})();
And then broadcast to C if you want to broadcast to all users in A and B.
This is just an example code, you could further improve this by not hard coding the roomnames but using an array or object instead to loop over possible room combinations.
As custom Adapter to make socket.broadcast.in("A").in("B").emit()work:
var Adapter = require('socket.io-adapter');
module.exports = CustomAdapter;
function CustomAdapter(nsp) {
Adapter.call(this, nsp);
};
CustomAdapter.prototype = Object.create(Adapter.prototype);
CustomAdapter.prototype.constructor = CustomAdapter;
CustomAdapter.prototype.broadcast = function(packet, opts){
var rooms = opts.rooms || [];
var except = opts.except || [];
var flags = opts.flags || {};
var packetOpts = {
preEncoded: true,
volatile: flags.volatile,
compress: flags.compress
};
var ids = {};
var self = this;
var socket;
packet.nsp = this.nsp.name;
this.encoder.encode(packet, function(encodedPackets) {
if (rooms.length) {
for (var i = 0; i < rooms.length; i++) {
var room = self.rooms[rooms[i]];
if (!room) continue;
for (var id in room) {
if (room.hasOwnProperty(id)) {
if (~except.indexOf(id)) continue;
socket = self.nsp.connected[id];
if (socket) {
ids[id] = ids[id] || 0;
if(++ids[id] === rooms.length){
socket.packet(encodedPackets, packetOpts);
}
}
}
}
}
} else {
for (var id in self.sids) {
if (self.sids.hasOwnProperty(id)) {
if (~except.indexOf(id)) continue;
socket = self.nsp.connected[id];
if (socket) socket.packet(encodedPackets, packetOpts);
}
}
}
});
};
And in your app file:
var io = require('socket.io')(server, {
adapter: require('./CustomAdapter')
});
io.sockets.adapter.room["Room A"].forEach(function(user_a){
io.sockets.adapter.room["Room B"].forEach(function(user_b){
if(user_a.id == user_b.id){
user_a.emit('your event', { your: 'data' });
}
});
});
This is more of a question regarding what to do in the scenario where you want to trigger a socket event for one user, that might be logged into another browser.
I've got a couple of functions that update a users' workstack real-time (in a queue of other workstacks that are assignable by other users); however, if the user is logged into another browser at the same time, and do an update in one browser, it doesn't update in the other (as they have a different socket.id).
I'm not sure what to do with this... I could do it based on the user ID of the person logged in, but at present my socket code does not have scope of any session variables and although there are modules such as session-socket - I'm not sure if that's the right path to go down.
Can anyone advise a way I might approach this please?
If you don't use any cluster you can follow the approach I had in my Miaou chat : I simply set the user as a property of the socket object and I iterate on sockets when necessary. This allow for a few utilitarian functions.
Here's the (simplified) related code.
io.on('connect', function(socket){
...
var userId = session.passport.user;
if (!userId) return die("no authenticated user in socket's session");
...
var shoe = new Shoe(socket, completeUser) // <=== bindind user socket here
// socket event binding here
The Shoe object :
// A shoe embeds a socket and is provided to controlers and plugins.
// It's kept in memory by the closures of the socket event handlers
function Shoe(socket, completeUser){
this.socket = socket;
this.completeUser = completeUser;
this.publicUser = {id:completeUser.id, name:completeUser.name};
this.room;
socket['publicUser'] = this.publicUser;
this.emit = socket.emit.bind(socket);
}
var Shoes = Shoe.prototype;
// emits something to all sockets of a given user. Returns the number of sockets
Shoes.emitToAllSocketsOfUser = function(key, args, onlyOtherSockets){
var currentUserId = this.publicUser.id,
nbs = 0;
for (var clientId in io.sockets.connected) {
var socket = io.sockets.connected[clientId];
if (onlyOtherSockets && socket === this.socket) continue;
if (socket && socket.publicUser && socket.publicUser.id===currentUserId) {
socket.emit(key, args);
nbs++;
}
}
return nbs;
}
// returns the socket of the passed user if he's in the same room
Shoes.userSocket = function(userIdOrName) {
var clients = io.sockets.adapter.rooms[this.room.id],
sockets = [];
for (var clientId in clients) {
var socket = io.sockets.connected[clientId];
if (socket && socket.publicUser && (socket.publicUser.id===userIdOrName||socket.publicUser.name===userIdOrName)) {
return socket;
}
}
}
// returns the ids of the rooms to which the user is currently connected
Shoes.userRooms = function(){
var rooms = [],
uid = this.publicUser.id;
iorooms = io.sockets.adapter.rooms;
for (var roomId in iorooms) {
if (+roomId!=roomId) continue;
var clients = io.sockets.adapter.rooms[roomId];
for (var clientId in clients) {
var socket = io.sockets.connected[clientId];
if (socket && socket.publicUser && socket.publicUser.id===uid) {
rooms.push(roomId);
break;
}
}
}
return rooms;
}
// returns the first found socket of the passed user (may be in another room)
function anyUserSocket(userIdOrName) {
for (var clientId in io.sockets.connected) {
var socket = io.sockets.connected[clientId];
if (socket.publicUser && (socket.publicUser.id===userIdOrName||socket.publicUser.name===userIdOrName)) {
return socket;
}
}
}
// closes all sockets from a user in a given room
exports.throwOut = function(userId, roomId, text){
var clients = io.sockets.adapter.rooms[roomId];;
for (var clientId in clients) {
var socket = io.sockets.connected[clientId];
if (socket.publicUser && socket.publicUser.id===userId) {
if (text) socket.emit('miaou.error', text);
socket.disconnect('unauthorized');
}
}
}
Real code
Now, with ES6 based node versions and WeakMap, I might implement a more direct mapping but the solution I described is robust and efficient enough.
This question title was really worded incorrectly, but I have no idea how to word it. If anyone wants to edit it, please feel free.
Basically I'm trying to figure out how to get a specific instance of a player via the socket that the data came from, this is going to be used to relay movements through my server for my small 2d project. Currently I have an object that's storing all of the players by the identification number, some people say it's an object, some say it's a hashmap implementation, some say it's an array, whatever the hell it is, I'm using it.
var connectedPlayers = {};
When a connection is created I create a new player like so:
var playerId = Math.floor(Math.random()*(50000-1+1)+1);
var userId = Math.floor(Math.random()*(50000-1+1)+1);
var player = new Player(playerId, "Guest"+userId, socket);
connectedPlayers[playerId] = player;
Obviously this is just generating random names/identification numbers for now, but that's perfectly fine, this is executed on the socket.on('connection', function() call.
If you need to see the Player.js script it's just a basic prototype script which sends information to all players about the player logging in, this all works properly.
var playerId;
var playerName;
var socket;
var positionX, positionY;
function Player(playerId, playerName, socket) {
this.playerId = playerId;
this.playerName = playerName;
this.socket = socket;
this.positionX = 250;
this.positionY = 250;
socket.emit('login', { playerID: playerId, playerX: this.positionX, playerY: this.positionY, playerName: playerName});
socket.broadcast.emit('player-connected',{ playerID: playerId, playerX: this.positionX, playerY: this.positionY, playerName: playerName} )
}
Player.prototype.getId = function() {
return this.playerId;
};
Player.prototype.getName = function() {
return this.playerName;
};
Player.prototype.getSocket = function() {
return this.socket;
};
Player.prototype.getX = function() {
return this.positionX;
};
Player.prototype.getY = function() {
return this.positionY;
};
My problem is, I need to be able to find out which Player belongs to a socket when the data comes in, because I want to authoritatively send movements to the clients. To do this I need to not give the client any control when it comes to which playerId it contains, because if I let the client tell the server, we could have other players moving at random.
The only way I've thought of is something like this, but it doesn't seem very efficient.
for(var i in connectedPlayers) {
if(conntectedPlayers[i].getSocket() == socket) {
// do stuff
}
}
EDIT: Adding the entire sockets.on('connection') due to a request from comments.
socketIO.sockets.on('connection', function(socket) {
console.log("Connection maid.");
socket.on('login', function(json) {
sqlConnection.query('select * FROM Accounts', function(err, rows, fields) {
if(err) throw err;
for(var row in rows) {
if(rows[row].Username == json.username) {
var playerId = Math.floor(Math.random()*(50000-1+1)+1);
var player = new Player(playerId, "Guest"+playerId, socket);
connectedPlayers[playerId] = player;
}
}
});
});
socket.on('move-request', function(json) {
socket.emit('move-request', {x: json.x, y: json.y, valid: true});
});
});
You can just add one or more properties to the socket object upon connection:
socketIO.sockets.on('connection', function(socket) {
console.log("Connection maid.");
socket.on('login', function(json) {
sqlConnection.query('select * FROM Accounts', function(err, rows, fields) {
if(err) throw err;
for(var row in rows) {
if(rows[row].Username == json.username) {
var playerId = Math.floor(Math.random()*(50000-1+1)+1);
var player = new Player(playerId, "Guest"+playerId, socket);
connectedPlayers[playerId] = player;
// add custom properties to the socket object
socket.player = player;
socket.playerId = playerId;
}
}
});
});
socket.on('move-request', function(json) {
// you can access socket.playerId and socket.player here
socket.emit('move-request', {x: json.x, y: json.y, valid: true});
});
});
So, now anytime you get an incoming message on a socket, you then have access to the playerId and player. In your code, when a message arrives, the socket variable for this message is in your parent scope and accessible so you can then get the player and playerId.
I've run into this situation a few times when storing "objects" in a C hash. I use a 32 bit hash function specifically tailed for pointers.
I'm not sure what you could use to uniquely identify the socket instance and suggest you see what underlying fields exist for that purpose. If you do find something that uniquely identifies the socket, you can feed that into a hash algorithm to generate a key. Of course, you would need a hash implementation (most likely in JavaSCript from the looks of it). I found this: http://www.timdown.co.uk/jshashtable/.
Good luck!
Is there a way to emit a message via socket.io while excluding some socket id?
I know about the existence of rooms, but that's a no-no for me.
If what I'm trying is impossible, what should I do between those two things:
a) Iterate over all users that I want to send a message (in fact, all of them except 1) and do a emit for each socket
or
b) Just emit the message to everyone and do something hacky on the client side to "ignore" the message.
EDIT: I can't do a broadcast because the message is generated from the server side (so there is no client interaction).
Rooms - are not the way to accomplish of what you are trying to do, as they are meant to be used only to limit groups of people, but not specific.
What you are looking for is simple collection of users and ability to set some sort of filter on them.
Iteration through list of sockets - is not that critical, as when you broadcast - it does iteration anyway.
So here is small class that keeps sockets, removes them when needed (disconnect) and sends message to all with optional exception of single socket.
function Sockets() {
this.list = [ ];
}
Sockets.prototype.add = function(socket) {
this.list.push(socket);
var self = this;
socket.on('disconnect', function() {
self.remove(socket);
});
}
Sockets.prototype.remove = function(socket) {
var i = this.list.indexOf(socket);
if (i != -1) {
this.list.splice(i, 1);
}
}
Sockets.prototype.emit = function(name, data, except) {
var i = this.list.length;
while(i--) {
if (this.list[i] != except) {
this.list[i].emit(name, data)
}
}
}
Here is example of usage, user sends 'post' message with some data, and server just sends it to all in collection except of original sender:
var collection = new Sockets();
io.on('connection', function(socket) {
collection.add(socket);
socket.on('post', function(data) {
collection.emit('post', data, socket);
});
});
In fact it can be used as rooms as well.