After every page updating I have +1 socket connection..
module.exports = function(io, client) {
var GameController = {
gamePage: function(req, res) {
client.hget('games', 'game.' + req.params.id, function (err, result) {
if (err) return result(err);
var game = JSON.parse(result);
io.on('connection', function (socket) {
console.log('send');
console.log(socket.id);
io.emit('get_bets', game.players);
});
res.render('pages/game', {
title: 'Game - ' + req.params.id,
user: req.user,
game: game
});
});
};
return GameController;
});
route file:
module.exports = function(io, client) {
var express = require('express');
var router = express.Router();
var GameController = require('controllers/GameController')(io, client);
router.get('/:id', GameController.gamePage);
...
return router;
};
Client side on react:
var Game = React.createClass({
getInitialState: function() {
this.socket = io();
return {
bets: null
}
},
socketGetBets: function() {
var that = this;
this.socket.on('get_bets', function(data) {
console.log('get bets');
that.setState({ bets: data });
});
this.socket.on('rand', function(data) {
console.log(data);
});
},
...
But after debug I find what problem not in client side.
app.js file:
var socket_io = require('socket.io');
var io = socket_io();
app.io = io;
//route
var game = require('./routes/games')(io, client);
bin/www file:
var server = http.createServer(app);
var io = app.io;
io.attach( server );
After page updating, io.on("connection") event show me "send" message in console, after second page updating, I have "send" "send", third update - "send" "send" "send" etc. Than Memory leak warning appeared. Console log socked.id show the same value many time.
Every time you call on, whether it's io.on or socket.on, you are registering an event handler. This being the case, you probably don't want to be calling io.on('connection') inside of a route, as you will register a new connection handler every time that route is accessed. This is why you are seeing cumulative messages being logged in the console.
In fact, you probably don't want to mix express routing with socket functions at all, as they are different protocols and will work independent of each other.
// server side
// this should only be called once per connection.
io.on('connection', function (socket) {
// handle socket protocol stuff.. fetching server data, sending data
socket.on('fetch bets', function() {
// get game.players from server
// updating other sockets
io.emit('get_bets', game.players);
})
})
app.get('/route', function (req, res) {
// handle http protocol stuff.. fetching server data, sending data
// send data back to caller
res.json(data)
})
The same applies to socket.on in your client side. It looks like you're adding a new 'get_bets' event handler everytime you call socketGetBets.
Instead you probably want to register that event handler one single time, likely in componentDidMount or componentWillMount. Also, because a socket connection can be considered global for your application, you can create the connection above your app.
// client side
var socket = io()
var Game = React.createClass({
getInitialState: function() {
return {
bets: null
}
},
componentWillMount: function() {
var that = this
socket.on('get_bets', function(data) {
that.setState({ bets: data })
})
}
...
Related
Tried different methods, but the data is sent to a maximum of one or two clients. How to send data to all the clients connected to the server ? What am I doing wrong?
Server.js:
var PORT = 3000;
var options = {
// 'log level': 0
};
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server, options);
server.listen(PORT);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/attantions/templates/.default/template.php');
});
io.sockets.on('connection', function (client) {
client.on('attantion', function (data) {
try {
// Tried so
io.sockets.volatile.emit('attantion', data);
// And tried so
io.sockets.emit('attantion', data);
client.emit('attantion', data);
client.broadcast.emit('attantion', data );
} catch (e) {
console.log(e);
client.disconnect();
}
});
});
Client.js:
socket.emit("attantion", data);
socket.on('attantion', function (data) {
pushData(data);
});
See this post for different options for socket.io messages
Send response to all clients except sender (Socket.io)
io.sockets.on('connection', function (client) {
client.on('attantion', function (data) {
//client.emit('attantion', data ); // This will send it to only the client
//client.broadcast.emit('attantion', data); // This will send it to everyone but this client
io.emit('attantion', data); // This will send it to all attached sockets.
});
});
Edit
I wonder if this post can help you?
Socket.io - Cannot load file
I was curious how sending the php file to the client through node.js works? are you using another framework?
Could you show more of what your client code looks like? loading the lib and the instantiation of the socket.
I'm using Socket.io in a chat application I'm building. I'm trying to setup the session so that I can persist the login of the users in between sessions.
Here's the relevant code :
index.js (server)
var cookieParser = require('cookie-parser')();
var session = require('cookie-session')({ secret: 'secret' });
app.use(cookieParser);
app.use(session);
io.use(function(socket, next) {
var req = socket.handshake;
var res = {};
cookieParser(req, res, function(err) {
if (err) return next(err);
session(req, res, next);
});
});
io.on('connection', function(socket){
socket.on('test 1', function(){
socket.handshake.test = 'banana';
});
socket.on('test 2', function(){
console.log(socket.handshake.test);
});
});
chat.js (client)
var socket = io();
socket.emit('test 1');
socket.emit('test 2');
The code above works, it will print banana to the console as expected. However, if I comment out the 'test 1' like so :
var socket = io();
// socket.emit('test 1');
socket.emit('test 2');
It will print undefined to the console.
Shouldn't it still be able to print banana, since it's using a session and it's persisting between requests? I also run into the same issue if I inverse the order in which I call 'test 1' and 'test 2'.
What am I doing wrong? What am I missing for the session to persist as expected?
The problem is that you are using a cookie-session, and socket.io does not allow you to set cookies due specification of XMLHttpRequest, also you don't have a request/response in the middle of a web socket transaction, so you cannot set the response cookie.
With your middleware, you can see the cookies, but you cannot set it in socket.io.
This can be ensured with this example:
io.use(function(socket, next) {
var req = socket.request;
var res = req.res;
cookieParser(req, res, function(err) {
if (err) return next(err);
session(req, res, next);
});
});
io.on('connection', function(socket){
var req = socket.request;
console.log(req.session.test)
socket.on('test 1', function(){
req.session.test = 'banana';
});
socket.on('test 2', function(){
console.log(req.session.test);
});
});
app.get('/', function(req, res) {
console.log(req.session.test)
if (!req.session.test) {
req.session.test = 'banana request'
}
})
You can use something like redis to persist the sessions, or some 'hack' to set it in the handshake.
Another option is not use sessions at all, and just set the property in socket object or in another javascript object, but in this case you cannot share the object between servers/services.
How to share sessions with Socket.IO 1.x and Express 4.x?
https://www.npmjs.com/package/express-socket.io-session
https://github.com/rakeshok/socket.io-client-cookie
http://www.w3.org/TR/XMLHttpRequest/
https://github.com/socketio/socket.io/blob/master/examples/chat/index.js#L35-L36
How to associate properties to socket.io object in Redis Store?
There is still access to headers.
You can save data on server side for each socket: socket.my_value = "any data type";
You can save data in io.sockets object, io.my_value = "any data type"
So, example:
io.on('connection', function(socket){
var id = socket.handshake.query.id; //you can set this in client connection string http://localhost?id=user_id
//Validate user, registered etc. and if ok/
if(validateUser(id)) {
//if this is reconnect
if(io.sessions[id]) {
socket.session_id = io.sessions[id].session_id;
} else {
var session_id = random_value;
socket.session_id = some_random_value;
io.sessions[id] = {
user_id: id,
session_id: socket.session_id
};
}
registerEvent(socket);
} else {
socket.emit('auth_error', 'wrong user');
}
});
function registerEvent(socket) {
socket.on('some_event', function(data, callback) {
if(!socket.session_id) {
callback('unathorized');//or emit
socket.emit('auth_error', 'unauthorized');
}
//do what you want, user is clear
});
}
I've looked at several answers on here, but I think they are referring to older versions of socket.io as their solutions have not worked for me. I'm getting the data back in the browser with
io.emit('update', data)
but it's emitting to all clients so the same data is showing up in multiple windows when I go to the same URL. Do I have to store the client id somewhere upon connection or can I just get it back before emitting? Please be specific. I tried a few other solutions from SO, but I got a lot of ReferenceError 'id' is not defined or sockets instead of socket.
Server set up and connection:
var app = express();
var server = require('http').createServer(app)
var io = require('socket.io')(server)
app.get('/aPath', function (req, res, next) {
res.writeHead(200)
var data = {
"val1": req.query.val1,
"val2": req.query.val2,
"val3": req.query.val3,
"val4": req.query.val4,
"val5": req.query.val5,
"val6": req.query.val6,
}
/*console.log(io.sockets.id)*/
//io.to(io.sockets.id).emit('update', data)
//io.sockets.socket(id).emit('update', data)
io.emit('update', data)
res.end("OK")
})
io.on('connection', function (socket) {
console.log('websocket user connected')
});
Since a third-party client is sending the info via a restful interface, you will need to include reference data for the client in that request in the form of a header or query string.
I suggest using Redis to store the active socket users for quick reference. This will allow you to have multiple applications in deployment that use a singular redis instance to keep the data in sync. You can also do the same in app memory, but that just doesn't scale well.
first, you need to use middleware to authenticate user and cache the socket.id
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var redis = require('redis');
io.use(function(socket, next){
// validate user
// cache user with socket.id
var userId = validatedUser;
socket.handshake.userId = userId;
redis.set(userId, socket.id, function (err, res) {
next()
});
});
next handle all socket communication
io.on('connection', function (socket) {
console.log('websocket user connected');
//next handle all socket communication
socket.on('endpoint', function (payload) {
//do stuff
socket.emit('endpoint.response', {/*data*/});
});
//Then remove socket.id from cache
socket.on('disconnect', function (payload) {
//remove user.id from cache
redis.del(socket.handshake.userId, function (err, res) {
console.log('user with %s disconnected', socket.id);
});
});
});
Handle third party event.
app.get('/aPath', function (req, res, next) {
// get user from third party
var userId = req.query.userId
var data = {
"val1": req.query.val1,
"val2": req.query.val2,
"val3": req.query.val3,
"val4": req.query.val4,
"val5": req.query.val5,
"val6": req.query.val6,
};
// get cached socketId from userId
redis.get(userId, function (err, socketId) {
// return ok to third party;
res.status(200).send("OK");
only emit if socketid still exists
if (err || !socketId) return;
// now emit to user
io.to(socketId).emit('update', data):
});
});
I am working on realtime data visualization application using node.js, express and socket.io.
Requirement:
Have to emit the events based on the client request.
For example: If user enter the url as http://localhost:8080/pages socket.io should emit the topic pages to client and another user request for http://localhost:8080/locations socket should emit location to that particular user.
Code
var server = app.listen("8080");
var socket = require('socket.io');
var io = socket.listen(server);
var config = {};
io.on('connection', function (socket) {
config.socket = io.sockets.socket(socket.id);
socket.on('disconnect', function () {
console.log('socket.io is disconnected');
});
});
app.get('/*', function(req, res) {
var url = req.url;
var eventName = url.substring('/'.length);
//pages and locations
config.socket.volatile.emit(eventName, result);
});
Client Code:
//No problem in client code.Its working correctly.
Sample code as follows
socket.on('pages', function (result) {
console.log(result);
});
Problem:
It is emitting pages and locations to both the clients.
Any suggestion to overcome this problem.
I couldn't understand your approach on this, but because you said you're rendering different pages, It means you can serve different code, so what about doing it like this:
Server Side:
var server = app.listen("8080");
var socket = require('socket.io');
var io = socket.listen(server);
var config = {};
app.get('/pages', function(req, res) {
res.render('pages.html');
});
app.get('/locations', function(req, res) {
res.render('locations.html');
});
io.on('connection', function (socket) {
socket.on('pagesEvent', function(data){
socket.volatile.emit('pages', {your: 'data'});
});
socket.on('locationsEvent', function(data){
socket.volatile.emit('locations', {your: 'data'});
});
});
On Client side:
pages.html:
socket.on('connect', function(){
socket.emit('pagesEvent', {});
});
socket.on('pages', function(data){
// do stuff here
});
locations.html:
socket.on('connect', function(){
socket.emit('locationsEvent', {});
});
socket.on('locations', function(data){
// do stuff here
});
You are doing it wrong, WebSockets supposed to work same in both directions. Client emit event to Server, server emit back to Client/Subscribers.
The way you are doing things, seems like a way of implementing API, but for some reason you are trying to implement it with WebSockets, instead of XHR.
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);