handle browser refresh socket.io - socket.io

I have a requirement using node js that handles disconnecting a user from a chat application.
I am not sure how to handle telling the difference between a browser closing and a user refreshing the browser.
client.on('disconnect', function () {
console.log( 'Disconnected' );
// run mysql code to remove user from logged in table
});
I have googled for a couple hours and cannot find a solution.
This seems like something pretty simple and I think it is the keywords that I am using.
Can someone point me in the right direction on how to handle this?
Thanks in advance.

One way would be to generate a random UID and save it to local storage. Right after the client connects, send this UID to the server and check to see if that UID exists as a connected user. On the server side, set a timeout in the disconnect that gives the user 15 seconds or so before their unique UID is deleted from the "users online" data.
Client:
// When the client starts, create the uid.
localstorage.setItem('uUID', Math.random().toString(24) + new Date());
// Emit the UID right after connection
socket.emit('userLogin', localstorage.getItem('uUID');
Server:
var currentUIDS = [];
var userIsConnected = true;
io.sockets.on('connection', function (socket) {
var currentUID = null;
socket.on('userLogin', function (data) {
if (data !== null) {
if (currentUIDS.includes(data)) {
userIsConnected = true;
currentUID = data;
}
}
});
socket.on('disconnect', function () {
userIsConnected = false;
setTimeout(function () {
if (!userIsConnected) currentUIDS.pop(currentUID);
}, 15000);
});
});

I have a better solution for that to handle multiple users:
var users = [],
users_connected = [];
io.on('connection', function(socket) {
var uid = null;
// register the new user
socket.on('register', function (user_uid) {
if ( users_connected.indexOf(user_uid) < 0 ) {
users_connected.push(user_uid);
}
if ( users.indexOf(user_uid) < 0 ) {
console.log('New user connected: ' + user_uid);
users.push(user_uid);
// notify other clients that a new user has joined
socket.broadcast.emit('user:join', {
name: user_uid,
users: users_connected.length
});
}
uid = user_uid;
});
// clean up when a user leaves, and broadcast it to other users
socket.on('disconnect', function () {
users_connected.splice( users_connected.indexOf(uid), 1);
setTimeout(function () {
if ( users_connected.indexOf(uid) < 0 ) {
socket.broadcast.emit('user:left', {
name: uid
});
var index = users.indexOf(uid);
users.splice(index, 1);
}
}, 3000);
});
});

Related

socket.io private updates in specific rooms

This question has been asked several times, but any of the answers is a solution to my problem.
issue:
There will be multiple accounts, and accounds will have multible users. In one specific account, the users will do some realtime updates - but the other accounts will be not affected from those real time changes, every accounts will be private.
We are planing to create room names for each account from cookie
Also we are using node in PHP project.
Sorry for my broken English!
// client side code
$( "#messageForm" ).submit( function() {
var nameVal = $( "#nameInput" ).val();
var msg = $("#messageInput").val();
socket.emit( 'message', { name: nameVal, message: msg } );
// Ajax call for saving datas
$.ajax({
url: "./ajax/insertMessage.php",
type: "POST",
data: { name: nameVal, message: msg },
success: function(data) {
}
});
return false;
});
socket.on( 'message', function( data ) {
var actualContent = $( "#messages" ).html();
var newMsgContent = '<li> <strong>' + data.name + '</strong> : ' + data.message + '</li>';
var content = newMsgContent + actualContent;
$( "#messages" ).append( newMsgContent );
});
//server side codes
var socket = require( 'socket.io' );
var express = require( 'express' );
var http = require( 'http' );
var app = express();
var server = http.createServer( app );
var io = socket.listen( server );
io.sockets.on( 'connection', function( client ) {
console.log( "New client !" );
client.on( 'message', function( data ) {
console.log( 'Message received ' + data.name + ":" + data.message );
//client.broadcast.emit( 'message', { name: data.name, message: data.message } );
io.sockets.emit( 'message', { name: data.name, message: data.message } );
});
});
server.listen( 8080 );
You can create private chat rooms as below.
Server side code
// usernames which are currently connected to the chat
var usernames = {};
// rooms which are currently available in chat
var rooms = ['room1','room2','room3'];
io.sockets.on('connection', function (socket) {
// when the client emits 'adduser', this listens and executes
socket.on('adduser', function(username){
// store the username in the socket session for this client
socket.username = username;
// store the room name in the socket session for this client
socket.room = 'room1';
// add the client's username to the global list
usernames[username] = username;
// send client to room 1
socket.join('room1');
// echo to client they've connected
socket.emit('updatechat', 'SERVER', 'you have connected to room1');
// echo to room 1 that a person has connected to their room
socket.broadcast.to('room1').emit('updatechat', 'SERVER', username + ' has connected to this room');
socket.emit('updaterooms', rooms, 'room1');
});
// 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.in(socket.room).emit('updatechat', socket.username, data);
});
socket.on('switchRoom', function(newroom){
// leave the current room (stored in session)
socket.leave(socket.room);
// join new room, received as function parameter
socket.join(newroom);
socket.emit('updatechat', 'SERVER', 'you have connected to '+ newroom);
// sent message to OLD room
socket.broadcast.to(socket.room).emit('updatechat', 'SERVER', socket.username+' has left this room');
// update socket session room title
socket.room = newroom;
socket.broadcast.to(newroom).emit('updatechat', 'SERVER', socket.username+' has joined this room');
socket.emit('updaterooms', rooms, newroom);
});
// 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');
socket.leave(socket.room);
});
});
Client side code
var socket = io.connect('http://localhost:8080');
// on connection to server, ask for user's name with an anonymous callback
socket.on('connect', function(){
// call the server-side function 'adduser' and send one parameter (value of prompt)
socket.emit('adduser', prompt("What's your name?"));
});
// listener, whenever the server emits 'updatechat', this updates the chat body
socket.on('updatechat', function (username, data) {
$('#conversation').append('<b>'+username + ':</b> ' + data + '<br>');
});
// listener, whenever the server emits 'updaterooms', this updates the room the client is in
socket.on('updaterooms', function(rooms, current_room) {
$('#rooms').empty();
$.each(rooms, function(key, value) {
if(value == current_room){
$('#rooms').append('<div>' + value + '</div>');
}
else {
$('#rooms').append('<div>' + value + '</div>');
}
});
});
function switchRoom(room){
socket.emit('switchRoom', room);
}
// on load of page
$(function(){
// when the client clicks SEND
$('#datasend').click( function() {
var message = $('#data').val();
$('#data').val('');
// tell server to execute 'sendchat' and send along one parameter
socket.emit('sendchat', message);
});
// when the client hits ENTER on their keyboard
$('#data').keypress(function(e) {
if(e.which == 13) {
$(this).blur();
$('#datasend').focus().click();
}
});
});
</script>
<div style="float:left;width:100px;border-right:1px solid black;height:300px;padding:10px;overflow:scroll-y;">
<b>ROOMS</b>
<div id="rooms"></div>
</div>
<div style="float:left;width:300px;height:250px;overflow:scroll-y;padding:10px;">
<div id="conversation"></div>
<input id="data" style="width:200px;" />
<input type="button" id="datasend" value="send" />
</div>
Reference Link:
http://psitsmike.com/2011/10/node-js-and-socket-io-multiroom-chat-tutorial/

socket io unique channel - emit without specifying the room

On my project each user is joining his unique channel (I know that the user is also in a socked id channel).
Client
var socket = io('http://localhost:8000/');
var room = "unique_id";
socket.on('connect', function () {
socket.emit('room', room);
});
socket.on("unique_id", function() {
console.log("test");
})
On the server side I am checking the rooms a user is connected to:
var io = require('socket.io')(8000);
io.on('connection', function(socket) {
var room;
socket.on('room', function(unique_id) {
socket.join(unique_id);
room = unique_id;
console.log(io.sockets.adapter.rooms); //show the unique_id room
io.to(room).emit('hello', 'msg');
});
socket.on(room, function(data) {
console.log(room);
})
});
Output on connect
{ 'DQOt2DX2IsHh-m9nAAAC': { sockets: { 'DQOt2DX2IsHh-m9nAAAC': true }, length: 1 },
'unique_id': { sockets: { 'DQOt2DX2IsHh-m9nAAAC': true }, length: 1 } }
Now I am using elephant.io to emit to the client as follows
$client = new Client(new Version1X('http://localhost:8000'));
$client->initialize();
$client->emit('unique_id', ['news' => 'bar']);
$client->close();
I think there is something wrong on my server side but I am not able to solve it. I need to process the events for the unique_id but how?
server side
var io = require('socket.io')(8000);
io.on('connection', function(socket) {
var room;
socket.on('room', function(address) {
socket.join(address);
room = address;
console.log(io.sockets.adapter.rooms);
io.to(room).emit('hello', 'msg');
});
socket.on('message', function(data) {
console.log(data.unique_id);
io.to(data.unique_id).emit('hello', 'msg');
})
});
elephant.io
$client->emit('message', ['unique_id' => 'foo']);
This is my solution which is working for me.

Laravel IO Socket and redis over SSL https connection

Currently have IO sockets with laravel broadcasting with redis working perfectly. Until I then set up an SSL cert on the domain.
I have redis-server running on port 3001.
Then there is a socket.js set to listen to 3000.
My JS on the page I listen via io('//{{ $listen }}:3000').
Any guidance would be great on how to get this working over https. Would I just use 443 as the port?
Thanks.
My socket.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
redis.subscribe('notifications', function(err, count) {
});
redis.on('message', function(channel, message) {
console.log('Message Recieved: ' + message);
message = JSON.parse(message);
io.emit(channel + ':' + message.event, message.data);
});
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
First, setup your serverOptions object:
var serverOptions = {
port: 3000,
host: 127.0.0.1, //address to your site
key: '/etc/nginx/ssl/your_site/server.key', //Or whatever the path to your SSL is
cert: '/etc/nginx/ssl/your_site/server.crt',
NPNProtocols: ['http/2.0', 'spdy', 'http/1.1', 'http/1.0']
}
For the NPNProtocols, you may not care for all of them, but they're provided for reference.
Now just create the server:
var app = require('https').createServer(serverOptions),
io = require('socket.io')(app);
This should be pretty plug and play into your source at his point.\
As a side note your stuff is completely wide open and anyone can listen on your web socket, so nothing should be sent that is private through here. If you need to make your data private, then you're going to need 1 of two things;
Something like JWT-Auth Token
Something custom that interface with the Redis queue:
Here's an example of the latter:
var SECRET_KEY = '<YOUR_LARAVEL_SECRET_KEY>';
var laravel_session_parser = {
ord: function (string) {
return string.charCodeAt(0);
},
decryptSession: function (cookie, secret) {
if (cookie) {
var session_cookie = JSON.parse(new Buffer(cookie, 'base64'));
var iv = new Buffer(session_cookie.iv, 'base64');
var value = new Buffer(session_cookie.value, 'base64');
var rijCbc = new mcrypt.MCrypt('rijndael-128', 'cbc');
rijCbc.open(secret, iv);
var decrypted = rijCbc.decrypt(value).toString();
var len = decrypted.length - 1;
var pad = laravel_session_parser.ord(decrypted.charAt(len));
return phpunserialize.unserialize(decrypted.substr(0, decrypted.length - pad));
}
return null;
},
getUidFromObj: function (obj, pattern) {
var regexp = /login_web_([a-zA-Z0-9]+)/gi;
if (pattern) {
regexp = pattern;
}
var u_id = null;
for (var key in obj) {
var matches_array = key.match(regexp);
if (matches_array && matches_array.length > 0) {
u_id = obj[matches_array[0]];
return u_id;
}
}
return u_id;
},
getRedisSession: function (s_id, cb) {
var _sessionId = 'laravel:' + s_id;
client.get(_sessionId, function (err, session) {
if (err) {
cb && cb(err);
return;
}
cb && cb(null, session);
});
},
getSessionId: function (session, _callback) {
var u_id = null,
err = null;
try {
var laravelSession = phpunserialize.unserialize(phpunserialize.unserialize(session));
u_id = laravel_session_parser.getUidFromObj(laravelSession);
} catch (err) {
_callback(err, null);
}
_callback(err, u_id);
},
ready: function (socket, _callback) {
if (typeof socket.handshake.headers.cookie === 'string') {
var cookies = cookie.parse(socket.handshake.headers.cookie);
var laravel_session = cookies.laravel_session;
var session_id = laravel_session_parser.decryptSession(laravel_session, SECRET_KEY);
laravel_session_parser.getRedisSession(session_id, function (err, session) {
if (!err && session) {
laravel_session_parser.getSessionId(session, function (err, user_id) {
if (user_id) {
_callback(null, session_id, user_id, laravel_session)
} else {
_callback(new Error('Authentication error'), null);
}
});
} else {
_callback(new Error('Authentication error'), null);
}
});
}
}
};
Now you can just have IO get an instance of the individuals session when they establish a connection to socket.io
io.on('connection', function (socket) {
laravel_session_parser.ready(socket, function(err, session_id, user_id, laravel_session) {
//log out the variables above to see what they provide
});
});
Note, I prefer to use dotenv in NodeJS to share environment variables between Laravel and Node.
Then you can do process.env.APP_KEY and you don't need to worry about sharing variables.
Also of note, that script above is not complete and is not production ready, it's just meant to be used as an example.

unable to connect XMPP server using stropher.js

When i connect to XMPP server using stropher.js it give connection status as 1 = The connection is currently being made
What is the problem for this status.
code is as below for connection.
it return me connecting status.
$(document).ready(function () {
$('#login_dialog').dialog({
autoOpen: true,
draggable: false,
modal: true,
title: 'Connect to XMPP',
buttons: {
"Connect": function () {
$(document).trigger('connect', {
jid: $('#jid').val(),
password: $('#password').val()
});
$('#password').val('');
$(this).dialog('close');
}
}
});
});
$(document).bind('connect', function (ev, data) {
var conn = new Strophe.Connection("http://127.0.0.1:5280/http-bind");
//"http://bosh.metajack.im:5280/xmpp-httpbind");
conn.connect(data.jid, data.password, function (status) {
if (status === Strophe.Status.CONNECTED) {
$(document).trigger('connected');
} else if (status === Strophe.Status.DISCONNECTED) {
Hello.log("Status DISCONNECTED.");
$(document).trigger('disconnected');
}
});
Hello.connection = conn;
});
$(document).bind('connected', function () {
// inform the user
Hello.log("Connection established.");
Hello.connection.addHandler(Hello.handle_pong, null, "iq", null, "ping1");
var domain = Strophe.getDomainFromJid(Hello.connection.jid);
Hello.send_ping(domain);
});
$(document).bind('disconnected', function () {
Hello.log("Connection terminated.");
// remove dead connection object
Hello.connection = null;
});
I am using phone gap.
Thanks
Your code at Stroph.Connection is wrong. First, you verify that passing URL parameter to Stroph.connnection("URL") is valid or not.
$(document).bind('connect', function (ev, data) {
var conn = new Strophe.Connection("http://example.com:7070/http-bind");
//"http://bosh.metajack.im:5280/xmpp-httpbind");
In the code above, 7070 is for unsecured port for HTTP binding connection on Openfire.
If your issue is not solved, please provide which XMPP server you are using in your PhoneGap application.

Modifying the session data from inside the socket.io callback

I am currently using this stack expres, socket.io, sessionstore. I followed the article here http://www.danielbaulig.de/socket-ioexpress/.
Well the problem is that i cannot modify the session values in socket.io callback.
Access from express side works well, the item get increased after each refresh.
app.get('/mysession', function(req, res) {
req.session.item++;
console.log(req.session);
res.render('session.jade', {
title: 'Sample title'
});
});
Using in socket.io side it does not and here is the problem, maybe i am setting the wrong object.
var io = io.listen(app);
io.sockets.on('connection', function(socket) {
var handshake = socket.handshake;
onlineCount++;
console.log('Well done id %s', handshake.sessionID);
handshake.session.item++;
console.log(handshake.session);
});
Here is bridge code.
io.set('authorization', function(data, accept) {
if (data.headers.cookie) {
data.cookie = parseCookie(data.headers.cookie);
data.sessionID = data.cookie['express.sid'];
sessionStore.get(data.sessionID, function(err, session) {
if (err || !session) {
accept('Error', false);
} else {
data.session = session;
accept(null, true);
}
});
} else {
return accept('No cookie tansmitted', false);
}
});
The only way I found to make this work is to grab the cookie from the request object on the connect event, parse it with your favourite cookie parser (I use connect.utils.parseCookie), and set it on that socket so that I may access it in future events:
socket.on('connection', function(client) {
var cookie = client.request.headers.cookie;
var pcookie = connect.utils.parseCookie(cookie);
var session_id = pcookie["connect.sid"];
if (session_id) {
sessionStore.get(session_id, function(err, sess) {
// do whatever you want with sess here
// ...
// if you want to "save" the session for future events
client.set('session_id', session_id);
}
}
});
The sessionStore API changed a little bit, now its sessionStore.load(sessionId, cb) instead of .get.

Resources