I am trying to log the event name and parameter for each event on my Node server. For this purpose I used
io.use(function(socket, next){
// how to get event name out of socket.
});
Now, I got stuck while trying to get event name and arguments. To me, it looks like common demand from API dev, so I am pretty sure there must be some way to the library to get that, I have tried to read the docs and source but I am not able to get the stuff.
The socket events needs to be handled properly,in any case if an event is not handled there will be no response.
var io = require('socket.io')(server);
var sessionMiddleWare=(session({secret: 'secret key', resave: true, saveUninitialized: true,cookie: { path: '/', httpOnly: true, maxAge: 300000 },rolling: true}));
app.use(sessionMiddleWare)
io.use(function(socket, next) {
sessionMiddleWare(socket.request, socket.request.res, next);
});
io.on('connection', function(socket) { // On Socket connection.
// inside this you can use different events
//event name and parameters can be found in socket variable.
console.log(socket.id) // prints the id sent from the client.
console.log(socket.data) // prints the data sent from the client.
// example event
socket.on('subscribe', function(room) { // Event sample.
console.log('joining room', room);
socket.room=room;
socket.join(room);
});
})
Hope this helps.
Related
My Sockets work fine when a client has the token (provided by Laravel jwt) but, a need to work with the clients while they aren't authenticated yet, I Want something like:
io.sockets
.on('connection', socketioJwt.authorize({
secret: process.env.JWT_SECRET,
timeout: 15000 // 15 seconds to send the authentication message
}))
.on('authenticated', function(socket) {
console.log('Authenticated!!');
// This works:
socket.on('setDataRegistered', function(datos) {
console.log('Registering!');
});
})
// This doesn't works:
.on('config', function(socket) {
console.log('A client wants to be configured before authenticated!');
})
How can I call from the FrontEnd to 'config' (socket.emit('config')) before authenticate??
Thanks for your help. Sorry my English.
What I do is this:
io.on('connection', function (socket) {
// Client connected but not authenticated. Client has to emit to 'authenticate' for authentication
socket.on('authenticate', function (data, fn) {
// Authenticate socket.
// define more events
});
// still accessible to unauthorised sockets
socket.on("other-event", function(data) {
});
});
I don't use any middleware for authentication. But that's just me. I never found the use for it.
I have been implementing the socket concept as in the https://github.com/techpines/express.io/tree/master/examples/sessions, I am able to display the session data that is stored once the page is initialised.
But nevertheless , when a session data keeps on changing over an interval , i get that session data emitted as undefined..
But similar concept is well done using socket.io authorization & handshake session provided the link http://www.danielbaulig.de/socket-ioexpress/
Client Side code :
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
// Emit ready event.
setInterval(function(){
socket.emit('ready', 'test');
},2000);
// Listen for get-feelings event.
socket.on('get-feelings', function () {
socket.emit('send-feelings', 'Good');
})
// Listen for session event.
socket.on('session', function(data) {
document.getElementById('count').value = data.count;
})
</script>
<input type="text" id="count" />
Server Side Code :
express = require('express.io')
app = express().http().io()
// Setup your sessions, just like normal.
app.use(express.cookieParser())
app.use(express.session({secret: 'monkey'}))
// Session is automatically setup on initial request.
app.get('/', function(req, res) {
var i = 0;
setInterval(function(){
req.session.count = i++;
console.log('Count Incremented as '+ req.session.count);
},1000);
res.sendfile(__dirname + '/client.html')
})
// Setup a route for the ready event, and add session data.
app.io.route('ready', function(req) {
req.session.reload( function () {
// "touch" it (resetting maxAge and lastAccess)
// and save it back again.
req.session.touch().save(function() {
req.io.emit('get-feelings')
});
});
/*
req.session.save(function() {
req.io.emit('get-feelings')
})*/
})
// Send back the session data.
app.io.route('send-feelings', function(req) {
console.log('Count Emitted as '+ req.session.count); // it is undefined in console
req.session.reload( function () {
// "touch" it (resetting maxAge and lastAccess)
// and save it back again.
req.session.save(function() {
req.io.emit('session', req.session)
});
});
})
app.listen(7076);
in console , it is printed as undefined in every emit ... i want to why socket session is not updated as the original user session keeps changing ... ???
Do i need to put any extra configuaration in the server side to handle changing session data ???
In a nodejs-express app, in the server.js file I set the socket-io connection.
It works fine doing something like this
var server = require('http').createServer(app)
var io = require('socket.io').listen(server);
io.sockets.on('connection', function (socket) {
// how can I save globally this 'socket'?
});
I would like to save this 'socket' globally in the server.js file and so, be able to use it everywhere in the project. Like this:
app.get('/on', function(req, res){
socket.on('test', function (data) {
console.log(data);
});
});
app.get('/emit', function(req, res){
socket.emit('test', "ciao");
});
I red there is a way to do it saving the 'socket' connection in session. My sessions settings:
app.configure(function(){
// ....
app.use(express.cookieParser(SITE_SECRET));
app.use(express.session({
secret :
,store : new MongoStore({
mongoose_connection : mongoose.connection
,db: mongoose.connections[0].db
})
,cookie: { maxAge: new Date(Date.now() + (1000*60*60*24*30*12)) }
}));
...
});
What is a good way to do it?
And also, after have made this saving, how to use socket.on() and socket.emit() loading the first page if the connection is not opened yet?
What you might have heard is not saving a socket into a session, but referencing sockets by their session cookie, which is passed to the server during the socket authorization process. During authorization, this is an example of the type of object that is passed to the server:
{
headers: req.headers, // <Object> the headers of the request
time: (new Date) +'', // <String> date time of the connection
address: socket.address(), // <Object> remoteAddress and remotePort object
xdomain: !!headers.origin, // <Boolean> was it a cross domain request?
secure: socket.secure, // <Boolean> https connection
issued: +date, // <Number> EPOCH of when the handshake was created
url: request.url, // <String> the entrance path of the request
query: data.query // <Object> the result of url.parse().query or a empty object
}
What we're interested in is the headers property, where we can find the session cookies of a connecting socket. We then parse the cookies during authorization:
// pass same objects from Express to Socket.IO so they match
var parseCookie = express.cookieParser(SITE_SECRET);
var store = new MongoStore({
mongoose_connection: mongoose.connection,
db: mongoose.connections[0].db
});
io.configure(function() {
io.set('authorization', function(handshake, callback) {
if (handshake.headers.cookie) {
parseCookie(handshake, null, function(err) {
// we used the signedCookies property since we have a secret
// save the session ID to the socket object, we can access it later
handshake.sessionID = handshake.signedCookies['connect.sid'];
store.get(handshake.sessionID, function(err, session) {
// we have the same Express session, reference it
socket.session = session;
callback(null, true);
});
});
} else {
// they client has no session yet, don't let them connect
callback('No session.', false);
}
});
});
app.use(parseCookie);
app.use(express.session({
secret: SITE_SECRET,
store: store,
cookie: {maxAge: new Date(Date.now() + (1000*60*60*24*30*12))}
}));
Then once we have saved the session ID, we can use the typical connection events:
var server = require('http').createServer(app)
var io = require('socket.io').listen(server);
var clients = {};
io.sockets.on('connection', function (socket) {
// save to a global object
var session = socket.handshake.sessionID;
clients[session] = socket;
socket.on('disconnect', function() {
delete clients[session];
});
});
Then we have a global reference by cookie signature. We can then access the socket like this:
app.get('/path', function(req, res) {
var socket = clients[req.sessionID];
socket.emit('Socket client accessed route.');
});
Keep in mind you might have to add some logic into your global logic for clients with multiple tabs, which would result in two sockets with the same authorization cookie.
As for your question about using socket.on() and socket.emit(), you can't use that before the connection has been established because the socket itself does not exist. If you want to send a message to all connected clients, then you should just use the global io.sockets object. It would then be more like io.sockets.emit().
Right now I have the following code: foo
sIo.sockets.on('connection', function(socket){
socket.emit('hello', 'world');
});
I would like to be able to emit this when somebody opens a page from my routes, like this:
//app.js
app.get('/send', routes.index);
//routes.js
exports.index = function(req, res){
socket.emit('hello', 'world');
};
How can I achieve this? Thanks in advance
To send a socket message to all connected sockets, you can just call io.sockets.emit instead of socket.emit. There are a few ways to send messages using socket.io which I'll outline below.
// Send the message to all connected clients
io.sockets.emit('message', data);
// Send the message to only this client
socket.emit('message', data);
// Send the messages to all clients, except this one.
socket.broadcast.emit('message', data);
There is also a concept of rooms which you can use to segment your clients.
// Have a client join a room.
socket.join('room')
// Send a message to all users in a particular room
io.sockets.in('room').emit('message', data);
All of that covers how to send messages, but it's clear now you're asking about how to access the socket and / or io objects from inside a separate file. One options just has you pass those dependencies to the specified file. Your require line will end up looking something like this.
var routes = require('./routes')(io);
Where io is the socket.io object created from .listen. To handle that in your route file you'll have to change how you're defining your exports.
module.exports = function(io) {
return {
index: function(req, res) {
io.sockets.emit('hello', 'world');
res.send('hello world');
}
};
}
A cleaner implementation would have your routes expose events that your socket code can bind to. The following is untested, but should be very close.
var util = require("util"),
events = require("events");
function MyRoute() {
events.EventEmitter.call(this);
}
util.inherits(MyRoute, events.EventEmitter);
MyRoute.prototype.index = function(req, res) {
this.emit('index');
res.send('hello world');
}
module.exports = new MyRoute();
And then in your app.js file when you're binding express routes and socket.io.
app.get('/send', routes.index);
routes.on('index', function() {
io.sockets.emit('hello', 'world');
});
There are many other ways to accomplish this, but the best one depends on what you're trying to do. As I alluded to before, calling broadcasting to everyone is going to be far more simple than broadcasting to a particular user.
I'm having a hard time putting all these three together, probably because I'm not getting properly the concept of routing with Express.
I have a RabbitMQ queue with event updates. We can recognize these events by their id. So I want to get on a given page about an event, just the updates corresponding to its id.
Queue: 1316, 1539, 3486, 3479, 1316, 3890, 3479, ... -> Feed from the DB indefinitely.
www.example.com/event/1316 -> Gets from the queue just messages with id 1316
www.example.com/event/3479 -> Gets from the queue just messages with id 3479
My code works good when I load the first event, but when I load the second one in a different window, it gets messages from both events, and if I load a third one, guess right, it gets messages from the three ids.
app.js
var express = require('express')
, http = require('http');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server, { log: false });
require('./io')(io);
var amqp = require('amqp');
var rabbitMQ = amqp.createConnection({ host: 'localhost' });
rabbitMQ.on('ready', function() {
console.log('Connected to RabbitMQ');
io.sockets.on('connection', function (socket) {
console.log('Socket connected: ' + socket.id);
rabbitMQ.queue('offer', { autoDelete: false, durable: false, exclusive: false }, function(q) {
q.bind('#'); // Catch all messages
q.subscribe(function (message) {
obj = JSON.parse(message.data.toString());
//socket.broadcast.to(obj.id).emit('message', obj);
io.sockets.in(obj.id).emit('message', obj);
});
});
});
});
var routes = require('./routes')
, event = require('./routes/event');
app.get('/', routes.index);
app.get('/event/:id', event.index);
server.listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
io.js
var socketio = function (io) {
if (!io) return socketio._io;
socketio._io = io;
}
module.exports = socketio;
routes/event.js
var io = require('../io')();
exports.index = function(req, res) {
io.sockets.on('connection', function (socket) {
socket.join(req.params.id);
});
res.render('event', { title: 'Event' });
};
Thanks!
You are receiving them all because you join but never leave the room. If you look at Socket IO Rooms from the wiki, at the bottom, it provides io.sockets.manager.roomClients[socket.id] as a way to get a list of rooms that the socket has joined (which I suspect will include all three if you've visited all three links).
You might want to try going through this list of rooms and leave any that aren't the current room, and see if that solves the problem.
Edit
Ok, so, there are two reasons/solutions to this. I just tested my theory, and it is valid - you receive messages for each room you've joined, and will continue to do so until you leave them. So here are the options:
1. leave all other rooms when they join a room
io.sockets.on('connection', function (socket) {
var room = req.params.id;
var roomKeys = Object.keys(io.sockets.manager.roomClients[socket.id]);
roomKeys.forEach(function(key) {
if (key === '' || key === '/' + room) return;
socket.leave(key.slice(1));
});
socket.join(room);
});
As said, I tested this. It works.
2. Don't send a message event, send a {room name} event
Instead of emitting a 'message' event, you could emit a '{room name}' event. Instead of your q.subscribe() callback containing io.sockets.in(obj.id).emit('message', obj);, you would just do socket.emit(obj.id, obj); and you'd have the javascript client only listen for that page's event types (based on the URL path).
I also tested this. It also works. It's also simpler (I think) because it only requires the .emit() in your q.subscribe() callback, which means you save the 'room management' stuff.
After trying and failing, I have understood what I was doing wrong cause using io.sockets.on('connection') inside of the router was duplicating the event. So at the end, the simplest way of thinking is the right one.
app.js
var room = '';
var roomHandler = function(req, res, next) {
if (req.path.match('event')) {
room = req.params.id;
}
next(); // Passing the request to the next handler in the stack.
}
io.sockets.on('connection', function (socket) {
socket.join(room);
});
rabbitMQ.on('ready', function() {
rabbitMQ.queue('offer', { autoDelete: false, durable: false, exclusive: false }, function(q) {
q.bind('#'); // Catch all messages
q.subscribe(function (message) {
obj = JSON.parse(message.data.toString());
io.sockets.in(obj.id).emit('message', obj);
});
});
});
app.get('/', routes.index);
app.get('/event/:id', roomHandler, event.index);