duplicate message received on redis sub - node.js

I am using express 3x, node.js and redis. when i as publishing message then 1 have receive this message 2-3 times in subscribe. (e.g. when i am refreshing my browser, message receive increase by 1 each time) .
below is my code.
server side :
~~~~~~~~~~
var express = require('express'),
http = require('http')
var redis = require('redis');
var redisCli = redis.createClient();
var redisPub = redis.createClient();
var redisSub = redis.createClient();
redisCli.on("error", function (err) {
console.error("\r\n Error generated from redis client ", err);
});
redisPub.on("error", function (err) {
console.error("\r\n Error generated from redisPub ", err);
});
redisSub.on("error", function (err) {
console.error("\r\n Error generated from redisSub ", err);
});
var server = http.createServer(app)
, io = require('socket.io').listen(server);
server.listen(process.env.PORT);
app.configure(function () {
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.set('view options', { layout: false });
app.use(express.favicon(__dirname + '/favicon.ico', { maxAge: 2592000000 }));
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: "myKey", store: new RedisStore({ maxAge: 86400000, client: redisCli }), cookie: { maxAge: 86400000} }));
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(__dirname + '/static'));
});
io.configure(function () {
io.enable('browser client minification'); // send minified client
io.enable('browser client etag'); // apply etag caching logic based on version number
io.enable('browser client gzip'); // gzip the file
io.set('log level', 1);
io.set("flash policy server", false);
io.set("transports", ["jsonp-polling", "xhr-polling"]);
});
io.sockets.on('connection', function (client) {
console.log("server - redisSub.subscribe from io.on.connection");
redisSub.unsubscribe();
redisSub.subscribe("announcement");
redisSub.on("message", function (channel, message) {
io.sockets.emit('announcement', message);
});
client.on('disconnect', function () {
redisSub.unsubscribe("announcement");
redisSub.quit();
});
});
app.post('/PublishMessage', function (req, res) {
redisPub.publish("announcement", req.body.users);
res.setHeader('Cache-Control', 'max-age=0, must-revalidate, no-cache, no-store');
res.setHeader('Connection', 'keep-alive');
res.contentType('application/json');
res.setHeader('Expires', new Date().addYears(-10));
res.json({ result: 'ok' });
});
Client side
~~~~~~~~~
this.socket = io.connect('http://XXX.XXX.X.XXX/', { transports: ['jsonp-polling', 'xhr-polling'] });
this.socket.on('connect', function () {
alert("client - Socket client connect");
});
this.socket.on('announcement', function (msg) {
alert("clientside - announcement ");
var nUsers = parseInt($('#Summary>article>p:last').text(), 10) + parseInt(msg, 10);
$('#Summary>article>p:last').text(nUsers);
});
=================================================================
So, any one guide me for the same !!!
thank you very much.

I have never used socket.io, but it looks to me like you're over complicating things with your connection handler.
Inside the handler, it doesn't seem like you're reacting to the connection (like emitting a "user connected" event) or modifying the behavior of the individual socket connection in any way.
What you are doing, is repeatedly subscribing and unsubscribing the one redisSub client. I could be wrong here, but I don't think you need to or should be doing that.
Rather you should sub "announcement" once, outside of the connection handler, as you don't need to sub/unsub this global client on every connection. Like:
// Move this subscription outside of the connection handler, and you shouldn't
// have to continue to sub/unsub or otherwise manage it.
redisSub.on("message", function (channel, message) {
io.sockets.emit('announcement', message);
});
// Since you're not reacting to connections or doing anything with individual
// connection sockets, you don't really have anything to do in this handler.
io.sockets.on('connection', function (socket) {
// if you ONLY wanted to emit to this socket, you'd do it here
//socket.emit("announcement", "just for this connection")
});

Related

Reactjs / Express: socket.io is not getting messages

The frontend is Reactjs:
componentDidMount(){
const socket = io.connect('ws://127.0.0.1:3001');
socket.on("connect", () => {
console.log('socket on connect; socket.id: ', socket.id); // Never printed!
});
socket.on("disconnect", () => {
console.log('socket on disconnect; socket.id: ', socket.id);
});
socket.on("hello", (res) => {
console.log('on hello: ', res); // Never printed!
});
socket.on("thank", (res) => {
console.log('on thank: ', res); // Never printed!
});
socket.emit("thank", "you");
};
The backend is Typescript / Express:
const app = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded());
app.use(express.urlencoded({ extended: true }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'build')));
... some custome middleware
var server = app.listen(3001);
// ------------------- configure Socket.io -------------------------
var io = require('socket.io').listen(server);
io.on("connection", function(socket: any) {
console.log("a user connected. socket: ", socket); // Never printed!
socket.emit('hello', 'world')
socket.emit('thank', 'you')
socket.on("thank", (res) => {
console.log('on thank: ', res); // Never printed!
});
});
// ----------------------------------------------------------------
I was hoping to send "world" and "you" on message "hello" and "thank" both from client to server and from server to client.
However, the line console.log("a user connected. socket: ", socket); was never executing on the server side.
Also the line console.log('socket on connect; socket.id: ', socket.id); was never printing on the client side.
Not to mention the "hello world" or "thank you" messages.
Moreover, I see a request of the form :
http://127.0.0.1:3001/socket.io/?EIO=4&transport=polling&t=Nk3yMzc
being made constantly and results in 404 not found
Question:
what is this GET request /socket.io/?EIO=4&transport=polling&t=****? What does it do? Why does it fire constantly?
The Express log is showing error:
RouteNotFoundError: Route '/socket.io/?EIO=4&transport=polling&t=Nk3zIhW' does not exist.
How can I solve this?
I'm making the Express app and the websocket share the port 3001. Some middlewares are designed for HTTP requests, and they are not for the websocket message. How can I make the websocket messages avoid the express middlewares?
The .listen function in your code below is to attach an httpServer to an already started WS server.
var io = require('socket.io').listen(server);
I think this should be:
var io = require('socket.io')(server);

Socket.io emit not working (by visiting URL)

I am developing an app that gets a signal from external hardware equipment. I catch this signal by redirecting it to a certain URL in my app: '/impulse/:id'.
I am able to catch the signal, but the emit function inside the app.get('/impulse/:id') is not triggering. The console logs are...
How can I make the emit function work?
Below is my server.js script, where I catch all the socket signals and prevent the external call from being redirected to the index page.
...
const express = require('express');
const app = express();
const port = process.env.PORT || 8080;
const socket = require('socket.io');
app.use(express.static(__dirname + '/public'));
app.use('/api', appRoutes);
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://HERE IS MY DB INFO...', function(err) {
if (err) {
console.log('Not connected to the database: ' + err); // Log to console if unable to connect to database
} else {
console.log('Successfully connected to MongoDB'); // Log to console if able to connect to database
}
});
var server = app.listen(port, function() {
console.log('Running the server on port ' + port); // Listen on configured port
});
var io = socket(server);
io.on('connection', function(socket){
socket.on('join', function(data){
var gameroom = data.gameid;
console.log("joined: " + gameroom)
socket.join(gameroom);
})
//FUNCTION I WANT TO TRIGGER
socket.on('impulse', function(data){
console.log('IMPULSE')
io.emit('impulseReceived', {
})
})
})
//PLACE WHERE I EMIT
app.get('/impulse/:id', function(req, res){
console.log('Impulse Received')
var time = req.query.TIME;
var gameroom = req.params.id;
io.on('connect', function (socket) {
socket.emit('impulse', {
})
})
res.json({ success: true, message: 'received the time!'})
})
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname + '/public/app/views/index.html')); // Set index.html as layout
});
Replace this whole
io.on('connect', function (socket) {
socket.emit('impulse', {
})
}
with this
io.emit('impulse', {})

Express websockets on message get cookie info

I have an express web socket application.
In the onmessage function, I would like to access the cookies of the client that sent the message.
The reason for this is that I'm making a game and I have the user login. I need to check what to name cookie is so that I control the correct player.
This is what I've got so far:
var express = require('express');
var expressWs = require('express-ws');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var app = express();
app.use(cookieParser('secretkey123'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}))
expressWs = expressWs(app);
app.get('/', function(req, res) {
// stuff for logging in
})
app.post('/', function(req, res) {
// stuff for logging in
})
app.get('/logout', function(req, res) {
res.clearCookie('name');
res.redirect('/');
// more stuff for logging in
})
app.ws('/ws', function(ws, req) {
ws.on('open', function() {
// how do I check when a connection is opened?
})
ws.on('message', function(msg) {
// who sent the message? how do I get the cookie info to check the user who send it?
})
ws.on('close', function() {
// the've disconnected
})
})
var server = app.listen(8000, function () {
var host = server.address().address
var port = server.address().port
})
Is this possible?
Also, where do I check when a websocket connection is opened?
I tried the 'open' event but it doesn't seem to be working.
Thanks for the help in advance!
I figured out how to do it!
I forgot that the req argument can be accessed inside the other functions.
This means in the on message function you can just do this:
ws.on('message', function(msg) {
req.cookies.username //do stuff
});
The connection open code can be done before you setup any of the events:
app.ws('/ws', function(ws, req) {
// connection open code here
ws.on('message', function(msg) {
// connection message code here
})
})

socket.io: disconnect event isn't fired

I have made a simple realtime visitor counter.
You can download it from this repository.
What happens is that disconnect event (even after browser closing) on server is never fired.
server.js is:
(function () {
var app, count, express, io;
express = require('express');
io = require('socket.io');
app = module.exports = express.createServer();
app.configure(function () {
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(require('stylus').middleware({
src: __dirname + '/public'
}));
app.use(app.router);
return app.use(express.static(__dirname + '/public'));
});
app.configure('development', function () {
return app.use(express.errorHandler({
dumpExceptions: true,
showStack: true
}));
});
app.configure('production', function () {
return app.use(express.errorHandler());
});
io = require('socket.io').listen(app);
count = 0;
io.sockets.on('connection', function (socket) {
count++;
io.sockets.emit('count', {
number: count
});
});
io.sockets.on('disconnect', function () {
console.log('DISCONNESSO!!! ');
count--;
io.sockets.emit('count', {
number: count
});
});
app.get('/', function (req, res) {
return res.render('index', {
title: 'node.js express socket.io counter'
});
});
if (!module.parent) {
app.listen(10927);
console.log("Express server listening on port %d", app.address().port);
}
}).call(this);
Script on the client is:
script(type='text/javascript')
var socket = io.connect();
socket.on('count', function (data) {
$('#count').html( data.number );
});
Put your on disconnect code inside your on connect block and edit it a bit like so:
io.sockets.on('connection', function (socket) {
count++;
io.sockets.emit('count', {
number: count
});
socket.on('disconnect', function () {
console.log('DISCONNESSO!!! ');
count--;
io.sockets.emit('count', {
number: count
});
});
});
This way you're detecting when a specific socket (specifically the socket you pass to your anonymous function that is run on connection) is disconnected.
From Socket.IO 1.0 the io.engine.clientsCount property is available. This property tells you how many open connection does your app currently have.
io.sockets.on('connection', function (socket) {
io.sockets.emit('count', {
number: io.engine.clientsCount
});
socket.once('disconnect', function () {
io.sockets.emit('count', {
number: io.engine.clientsCount
});
});
});
Note: Use .once instead of .on and the listener will be removed automatically from the socket what is good for us now, because the disconnect event is only fired once per socket.
Just in case anyone else made this silly mistake: make sure that any socket middleware you've defined calls next() at the end, or else no other socket handlers will run.
// make sure to call next() at the end or...
io.use(function (socket, next) {
console.log(socket.id, "connection middleware");
next(); // don't forget this!
});
// ...none of the following will run:
io.use(function (socket, next) {
console.log(socket.id, "second middleware");
next(); // don't forget this either!
});
io.on("connection", function (socket) {
console.log(socket.id, "connection event");
socket.once("disconnect", function () {
console.log(socket.id, "disconnected");
});
});

Grabbing existing session on page reload with express/connect-redis

Using connect, express, and socket.io, I'm trying to allow my application to grab the session details when reconnecting. My sessions obviously work while the client is connected, but if I refresh the page on my browser, it forgets everything.
My session cookie is definitely the same, so it's not that.
My code's a big mish-mash of snippets I've taken from a number of different sources, since there doesn't seem to be one complete example application out there. :-/
What am I missing..?
var qs = require('querystring'),
express = require('express'),
app = express.createServer(),
io = require('socket.io').listen(app.listen(8000)),
routes = require('./routes'),
pCookie = require('connect').utils.parseCookie,
Session = require('connect').middleware.session.Session,
RedStore= require('connect-redis')(express),
sStore = new RedStore();
// Configuration
app.configure(function(){
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(express.session({ store: sStore, secret: 'tehsecretz' }));
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(express.errorHandler());
});
// Routes
app.get('/', routes.index);
io.sockets.on('connection', function (client) {
var hs = client.handshake,
session = hs.session;
console.log('A socket connected called: ' + hs.sessionId);
var intervalId = setInterval(function() {
hs.session.reload(function() {
hs.session.touch().save();
});
}, 60 * 1000);
if (!session.userName) {
// Prompts the user for a name on the frontend
client.emit('need-to-register');
}
client.on('message', function(msg, c) {
console.log(session);
console.log(msg);
});
client.on('register-user', function(data, fn) {
// This retrieves the user's name
// and - hopefully - saves it to the session.
data = qs.parse(data);
session.userName = data.username;
hs.session.save();
console.log('Saving: ', session);
fn('ok');
});
client.on('disconnect', function() {
clearInterval(intervalId);
});
});
io.set('authorization', function (data, accept) {
if (data.headers.cookie) {
data.cookie = pCookie(data.headers.cookie);
data.sessionId = data.cookie['connect.sid'];
data.sessionStore = sStore;
sStore.get(data.sessionId, function (err, session) {
if (err || !session) {
accept('Error', false);
} else {
console.log(data.sessionId, session);
data.session = new Session(data, session);
accept(null, true);
}
});
} else {
return accept('No cookie transmitted', false);
}
});
Thanks for any help provided!
Ugh. So in Daniel Baulig's post on the subject, he referenced the identifier sessionID. I figured that was just poor convention (as I'm used to camelCase) and promptly changed it to sessionId in my code.
As I was debugging, I turned on MONITORing on my redis instance and noticed the session was being written to sess:undefined.
Turns out sessionID is special and is referenced internally as the actual identifier. Changing all instances of sessionId to sessionID allows me to have a better clue as to who is connecting!
Add 'cookie' in "express.session"
app.use(express.session({
secret: 'exampleSecretKey'
,store: exampleStore
,key: 'example'
cookie: {
path: '/'
,expires: false // Alive Until Browser Exits
,httpOnly: true
// ,domain:'.example.com'
}
});

Resources