I'm using socket.io on top of Express/Node
Right now I'm using this method:
// get room name from URL
var requestRoom;
app.get('/editor/:room', function(req, res) {
requestRoom = req.params.room;
res.render('editor/editor', {
title: 'Editor'
});
});
// use that room name for private rooms within socket.io
io.on('connection', function(socket) {
socket.join(requestRoom);
// rest of socket.io code....
to grab the room-name from a URL that looks like sitename.io/editor/[room-name].
Then I'm using requestRoom as a variable for socket.to(requestRoom).broadcast.. for sending the socket packets to a specific room.
The problem
Each time a new user visits the app, requestRoom gets repopulated for all users, instead of just for the user that just joined.
Is there a way to start a new 'instance' with own variables for each 'session'?
What am I missing here?
You cannot create new instance for each session.
But, you can solve this issue by sending the room name from the client.
When you connect to WebSocket using socket.io-client, add the room name to query parameter.
Client:
var requestRoom = "roomName";
io('/', {query: "requestRoom=" + requestRoom})
Server:
io.on('connection', function(socket) {
var requestRoom = socket.request._query.requestRoom;
socket.join(requestRoom);
// rest of socket.io code....
EDIT:
Nicholas Kyriakides used simpler way to solve this.
Server:
io.on('connection', function(socket) {
var refererSplit = socket.request.headers.referer.split('/');
var requestRoom = refAr[refererSplit.length-1]; //Considering Room name is at the end of the path.
socket.join(requestRoom);
// rest of socket.io code....
No need to pass roomName from the client.
Related
This is a pretty simple question but i want to make sure that i am scaling our socket.io implementation correctly. We are using socket.io to respond back to the client after a lengthy process on the nodejs backend. So basically client makes call, then socket.io signals the client that the process has completed. Also socket.io ONLY responds to a temporary room that was established for the request.
In nodejs i created a global variable for the following so that i could emit back to the client room:
global.io = require('socket.io')(server);
But to create the room itself I am a little unsure how to create it globally such that only the socket that connected and made the request receives the response.
So if i have 500 client machines that initiate a connection through socket.io, each one will have its own socket. To ensure that the rooms are unique i use a guid across all 500. Of course i do not want all sockets to receive traffic if only one socket for a specific room is supposed to be evaluating the emit....
any ideas?
If I understood your question correctly, you're looking to send information to that 1 socket?
Perhaps something like this:
socket.broadcast.to(socketId).emit('someeventname', eventData);
If you have the connection open with that client, that means you have their socket id through socket.id . You can emit events to just that socket.
const app = express();
var http = require("http");
var server=http.createServer(app).listen(2525, (req, res) => {
console.log("Server running on", 2525);
});
var socketIO = require("socket.io");
var io = socketIO(server);
global.io = io
io.on("connection", async (socket) => {
socket.on("joinrooms", async (data) => {
socket.join(data.userId);
});
socket.on("sendMessage", async (data) => {
console.log("message", data);
io.to(data.touserId).emit("sendMessage", data);
});
});
/* Must Read section
Joinrrom data sample
data={
userId:123 //User's unique id.
}
sendMessage data sample
data={
userId:123, //sender User's unique id.
touserId:456, //reciver User's unique id.
}
Here I'm creating a room from the user's unique id(stored in DB) so whenever I
want to send data to a particular user I will emit an
event("io.to(data.touserId).emit") using the user's
a unique id that way only specific users will get messages.
*/
In my app.js I have
var app = express();
var serv = http.createServer(app);
var io = require('socket.io').listen(serv);
io.sockets.on('connection', function(socket) {
//some code here
}
var SessionSockets = require('session.socket.io'),
sessionSockets = new SessionSockets(io, express_store, cookieParser);
sessionSockets.on('connection', function (err, socket, session) {
//set up some socket handlers here for the specific client that are
//only called when a client does a socket.emit.
//These handlers have access to io, sessionSockets, socket, session objects.
}
How can the express routes access a particular client's socket reference after processing a post/get which is not triggered by a client socket.emit but triggered by a client post/get. What is the best way to scope the socket.io server(io/sessionSockets)/client(socket) objects in routes so that I can get this client's socket reference easily?
These three steps helped me to the solve the problem. This identifies tabs also uniquely as that was one of my requirements.
On connection, join using socket.id and then send the socket.id back to the client using
io.sockets.on('connection', function(socket) {
socket.join(socket.id);
socket.emit('server_socket_id', {socket_id : socket.id});
}
Client receives the emit event using
socket.on('server_socket_id', function(data){
//assign some global here which can be sent back to the server whenever required.
server_socket_id = data.socket_id;
});
In app.js I fetch the corresponding socket like this and pass it on to the routes.
app.post('/update', function(req, res){
var socket_id = req.body.socket_id;
route.update(req, res, io.sockets.in(socket_id).sockets[socket_id]);
});
The best way to do this is to use the socket.io authorization setting, although the module session.socket.io was created specifically for that purpose. Each time a socket establishes a connection, there is handshake data that is stored (although I've heard that flashsockets won't pass the browser cookies). This is what it looks like (and is similarly written in the module you're using):
io.configure(function () {
io.set('authorization', function (handshakeData, callback) {
//error object, then boolean that allows/denies the auth
callback(null, true);
});
});
What you could do from here is parse the cookie, then store a reference to that socket by the cookie name. So you would add this to the authorization setting:
var data = handshakeData;
if (data.headers.cookie) {
//note that this is done differently if using signed cookies
data.cookie = parseCookie(data.headers.cookie);
data.sessionID = data.cookie['express.sid'];
}
Then, when you listen on connections, store the client by session identifier:
var clients = {};
io.sockets.on('connection', function(socket) {
//store the reference based on session ID
clients[socket.handshake.sessionID] = socket;
});
And when you receive an HTTP request in Express, you can fetch it like this:
app.get('/', function(req, res) {
//I've currently forgotten how to get session ID from request,
//will go find after returning from school
var socket = clients[sessionID];
});
is there any working example which store sessions and use that sessions for all opened windows to let one user just connect to one room just once?
this app will get phpsessions as node.js sessions but i cant findout how to just let one person to access this chat application just once
//define what we need
var express = require('express'),
app = express(),
memcache = require("memcache"),
http = require('http'),
server = http.createServer(app),
io = require('socket.io').listen(server),
co = require("./cookie.js"),
php = require('phpjs'),
codein = require("node-codein");
// answer all request from users and send them back index.html for root access
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
var cookieManager = new co.cookie(req.headers.cookie);
//using memcache as our session store
var client = new memcache.Client(11211, "localhost");
//connect to memcache client
client.connect();
//get our cookie sessions
user = client.get("sessions/"+cookieManager.get("sec_session_id"), function(error, result){
var session = JSON.parse(result);
//get just username from sessions(sessions store name and family and chat username in this case)
user = JSON.parse(session.name);
user = user.username;
//use this function to pass our chat username to our function
storeUsername(user);
});
});
function storeUsername(user){
// usernames which are currently connected to the chat
var usernames = {};
io.sockets.on('connection', function (socket) {
usernames[socket.id] = socket;
// when the client emits 'sendchat', this listens and executes
socket.on('sendchat', function (data) {
// we tell the client to execute 'updatechat' with 2 parameters
io.sockets.emit('updatechat', socket.username, data);
});
// when the client emits 'adduser', this listens and executes
socket.on('adduser', function(username){
// we store the username in the socket session for this client
socket.username = user;
// add the client's username to the global list
// echo to client they've connected
if(php.in_array(socket.username,usernames)){
delete usernames[socket.username];
}else{
usernames[user] = user;
console.log('not exist');
socket.emit('updatechat', 'SERVER', 'you have connected');
// echo globally (all clients) that a person has connected
socket.broadcast.emit('updatechat', 'SERVER', username + ' has connected');
// update the list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
}
});
// when the user disconnects.. perform this
socket.on('disconnect', function(){
// remove the username from global usernames list
delete usernames[socket.username];
// update list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
// echo globally that this client has left
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
});
});
}
server.listen(3000);
everything works fine and user which send data is defined but when i access this site in another tab i will connect another time to socket.io server
Do you mean sharing a websocket between browser tabs?
Sharing websocket across browser tabs?
But why do you need to share sockets? I've developed a node.js chat for a forum and we don't care about how many sockets a user has. We just have a "User" object that has a list of sockets. We don't care if the sockets come from firefox, from an android app... it's not a problem. And when we need to send information to a user, we send it to each socket.
You can try this. It is using express and socket.io https://github.com/gravityonmars/Balloons.IO
I am trying encapsule a socket.io instance in a module. I did this but it looks a bit messy because i have to inject some dependencies for authentication of the socket transport from express / passport.
My problem is i want to access the socket instance outside of the module like socket.on("newDevice", function (data) {});
The socket instance which i get by the connection event is inside the function and it even may not exist on creation because no connection is established. This simply looks kind of wrong to me. I dont want to inject more and more depencies just because i need them inside of the function scope.
I thought about doing the sio.on('connection', function(socket) {}); outside of the module. And maybe i could do it twice, first inside of the module and then outside but i would create two listeners i guess.
Is there any good practice or pattern how to do this properly?
var io = require('socket.io');
var socket = function (server, sessionStore, cookieParser, authentication) {
var sio = io.listen(server);
// Configure socket.io
sio.configure(function () {
// Authorize the socket.io request
sio.set('authorization', function (data, accept) {
// Authorization is done here
});
});
sio.on('connection', function(socket) {
var lastActionTime = new Date();
// Get the userId from the session
var session = socket.handshake.session;
var userId = session.passport.user;
var sessionID = socket.handshake.sessionID;
var userdata = null;
// Deserialize user by the userId
authentication.deserializeUser(userId, function(err, user) {
// get the userdata
});
socket.on('disconnect', function() {
});
socket.on('brightnessChange', function(data) {
// TODO Do something here device control
// Broadcast to other devices
this.broadcast.emit('brightnessChange', data);
});
});
return sio;
};
module.exports = socket;
I would suggest, the below, flexibility and scales well. I've tried both ways and resulted in using multiple connection events.
I thought about doing the sio.on('connection', function(socket) {}); outside of the module. And maybe i could do it twice, first inside of the module and then outside but i would create
two listeners i guess.
I want to build a chat system in nodeJs + MYSQL using php. It will be private chat one to one and will save chat in database. Anyone know from where I need to start.
Currently I got this code for SERVER:
var app = require('express').createServer()
var io = require('socket.io').listen(app);
app.listen(8181);
// routing
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
// usernames which are currently connected to the chat
var usernames = {};
io.sockets.on('connection', function (socket) {
// when the client emits 'sendchat', this listens and executes
socket.on('sendchat', function (data) {
// we tell the client to execute 'updatechat' with 2 parameters
io.sockets.emit('updatechat', socket.username, data);
});
// when the client emits 'adduser', this listens and executes
socket.on('adduser', function(username){
// we store the username in the socket session for this client
socket.username = username;
// add the client's username to the global list
usernames[username] = username;
// echo to client they've connected
socket.emit('updatechat', 'SERVER', 'you have connected');
// echo globally (all clients) that a person has connected
socket.broadcast.emit('updatechat', 'SERVER', username + ' has connected');
// update the list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
});
// when the user disconnects.. perform this
socket.on('disconnect', function(){
// remove the username from global usernames list
delete usernames[socket.username];
// update list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
// echo globally that this client has left
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
});
})
There are two ways. Thirst you can hold references to all sockets in an array (all at least IDs of these sockets). When a user emits private message you search the array for target socket and send it to this particular one. This requires to hold some kind of ID of a socket. You may use inner socket.id but it will be a problem when the client reconnects (new ID generated). And there is another problem when your app works on more then one machine (they cannot share arrays of connected clients).
The second way is to use rooms. Whenever client connects I suppose he has a name, for example John. Then you can use something like this for his connection:
socket.join('/priv/'+name);
Now this creates a room and adds socket to it. If you want to send message to John then you simply use
io.sockets.in('/priv/John').emit('msg', data);
At that point you can be sure that the message went exactly to the socket in /priv/John room. This works perfectly with Redis combined with socket.io (to avoid many machines problem) and session authorization. I didn't try it with memoryStore, but it should work as well.
Also you don't have to worry about rooms when clients disconnect. Socket.io automatically destroys empty rooms.