Reconnect socket in disconnect event - node.js

I am trying to reconnecct the socket after the disconnect event is fired with same socket.id here is my socket config
var http = require('http').Server(app);
var io = require('socket.io')(http);
var connect_clients = [] //here would be the list of socket.id of connected users
http.listen(3000, function () {
console.log('listening on *:3000');
});
So on disconnect event i want to reconnect the disconnected user with same socket.id if possible
socket.on('disconnect',function(){
var disconnect_id = socket.id; //i want reconnect the users here
});

By default, Socket.IO does not have a server-side logic for reconnecting. Which means each time a client wants to connect, a new socket object is created, thus it has a new id. It's up to you to implement reconnection.
In order to do so, you will need a way to store something for this user. If you have any kind of authentication (passport for example) - using socket.request you will have the initial HTTP request fired before the upgrade happened. So from there, you can have all kind of cookies and data already stored.
If you don't want to store anything in cookies, the easiest thing to do is send back to client specific information about himself, on connect. Then, when user tries to reconnect, send this information again. Something like:
var client2socket = {};
io.on('connect', function(socket) {
var uid = Math.random(); // some really unique id :)
client2socket[uid] = socket;
socket.on('authenticate', function(userID) {
delete client2socket[uid]; // remove the "new" socket
client2socket[userID] = socket; // replace "old" socket
});
});
Keep in mind this is just a sample and you need to implement something a little bit better :) Maybe send the information as a request param, or store it another way - whatever works for you.

Related

Websockets & NodeJS - Changing Browser Tabs & Sessions

I've started writing a node.js websocket solution using socket.io.
The browsers connects to the node server successfully and I get see the socket.id and all config associated with console.log(socket). I also pass a userid back with the initial connection and can see this on the server side to.
Question: I'm not sure the best way to associate a user with a connection. I can see the socket.id changes every page change and when a tab is opened up. How can I track a user and send 'a message' to all required sockets. (Could be one page or could be 3 tabs etc).
I tried to have a look at 'express-socket.io-session' but I'm unsure how to code for it and this situation.
Question: I have 'io' and 'app' variables below. Is it possible to use the 2 together? app.use(io);
Essentially I want to be able to track users (I guess by session - but unsure of how to handle different socket id's for tabs etc) and know how to reply to user or one or more sockets.
thankyou
The best way to handle the situation is rely on SocketIO's rooms. Name the room after the user's unique ID. This will support multiple connections out of the box. Then, whenever you need to communicate with a particular user, simply call the message function and pass in their id, the event, and any relevant data. You don't need to worry about explicitly leaving a room, SocketIO does that for you whenever their session times out or they close their browser tab. (We do explicitly leave a room whenever they log out though obviously)
On the server:
var express = require('express');
var socketio = require('socket.io');
var app = express();
var server = http.createServer(app);
var io = socketio(server);
io.on('connect', function (socket) {
socket.on('userConnected', socket.join); // Client sends userId
socket.on('userDisconnected', socket.leave); // Cliend sends userId
});
// Export this function to be used throughout the server
function message (userId, event, data) {
io.sockets.to(userId).emit(event, data);
}
On the client:
var socket = io('http://localhost:9000'); // Server endpoint
socket.on('connect', connectUser);
socket.on('message', function (data) {
console.log(data);
});
// Call whenever a user logs in or is already authenticated
function connectUser () {
var userId = ... // Retrieve userId somehow
if (!userId) return;
socket.emit('userConnected', userId);
}
// Call whenever a user disconnects
function disconnectUser () {
var userId = ... // Retrieve userId somehow
if (!userId) return;
socket.emit('userDisconnected', userId);
}

How to send messages by socket.io to specific logged in users after certain events?

I take a few step into socket.io. It's very hard to code without req, because I don't know response to who.
For this question. My website have some blogs. When the blogs updated , I need to 'tell' users who followed the blogs that the blogs have been updated. The problems are I don't know witch followed users are online and I don't know how to emit events to them with only followed user id.
Can some one give me some tips? No need any specific code.
You can call join to subscribe the socket to a given channel:
io.on('connection', function(socket){
socket.join('some room');
});
And then simply use to when broadcasting or emitting:
io.to('some room').emit('some event');
To leave a channel, you need to call leave :
io.on('disconnect', function(socket){
socket.leave('some room');
});
You can make use of cookies to map http requests to corresponding socket connection. Lets say we are using cookie sesson-id.
Drop this cookie on client on a http request.
In the following patch of code, before creating a new socket connection, you attach session-id cookie to the socket header. This way you can access this session-id in .on("connection") event:
var io = require("socket.io");
sio = io.listen(server);
sio.set("authorization", function (data, accept) {
if (data.headers.cookie) {
data.cookie = cookie.parse(data.headers.cookie);
data.headers.sessionID = data.cookie["session-id"];
}
accept(null, true);
});
Next, you store socket connection objects associated to their session-id:
var sockets = {}; // store all socket connections here
sio.sockets.on("connection",connected);
function connected(socket){
var sessionID = socket.handshake.headers.sessionID;
sockets[sessionID] = socket;
}
Now, whenever you wish to emit socket event related to a particular http request:
function dummyReq(req,res){
var session_id = // get cookie from req
//after certain actions
sockets[session_id].emit("dummy-event","dummy-message");
}
In your case you will have to emit socket events for all session-id linked to users who are following those blogs

Destroying a handshake after logout. socket.io

Hello I am trying to build chat into an application. What I am wondering is when the user logs out of the website how do I also destroy the socket.io handshake associated with that session so the user cannot send messages from say another tab when he is logged out.
I am using expressjs if that is any help.
Well in case anyone ever find this and wants to know I did figure it out.
You can access the sockets disconnect function. I had object of users ids and their socket id so when someone logged out I called
app.get("/logout", function(req,res){
//do other logging out stuff
sockets.disconnectUser(req.session.user_id);
}
// Disconnect User function
sockets.disconnectUser = function(user_id){
sockets.socket(users[user_id]).disconnect();
}
The socket.io object contains information about all connected sockets and the sessionID of each socket. Thus, it is possible to iterate through the connected sockets and disconnect those which are associated with the sessionID that is logging out. There is no need to manually track user and socket ids in this approach.
Example code tested with socket.io#2.2.0, express#4.17.1 and express-session#1.16.2.
const SocketIO = require('socket.io');
let sio = new SocketIO;
app.get('/logout', function(req, res) {
//do other logging out stuff
logoutSocketsIO(req.sessionID);
});
// Iterate through all connected sockets and close those which are associated
// with the given sessionID
// Note: One sessionID can have multiple sockets (e.g. many browser tabs)
function logoutSocketsIO(sessionID) {
let connections = sio.sockets.connected;
for(let c in connections) {
let socketSessionID = connections[c].conn.request.sessionID;
if(sessionID === socketSessionID) {
connections[c].disconnect();
}
}
}

How can I send packets between the browser and server with socket.io, but only when there is more than one client?

In my normal setup, the client will emit data to my server regardless of whether or not there is another client to receive it. How can I make it so that it only sends packets when the user-count is > 1? I'm using node with socket.io.
To do this you would want to listen to the connection event on your server (as well as disconnect) and maintain a list of clients which are connected in a 'global' variable. When more than 1 client is connected send out a message to all connected clients to know they can start sending messages, like so:
var app = require('express').createServer(),
io = require('socket.io').listen(app);
app.listen(80);
//setup express
var clients = [];
io.sockets.on('connection', function (socket) {
clients.push(socket);
if (clients.length > 1) {
io.socket.emit('start talking');
}
socket.on('disconnect', function () {
var index = clients.indexOf(socket);
clients = clients.slice(0, index).concat(clients.slice(index + 1));
if (clients.length <= 1) {
io.sockets.emit('quiet time');
};
});
});
Note: I'm making an assumption here that the socket is passed to the disconnect event, I'm pretty sure it is but haven't had a chance to test.
The disconnect event wont receive the socket passed into it but because the event handler is registered within the closure scope of the initial connection you will have access to it.

Sending message to specific client with socket.io and empty message queue

I´m going crazy with socket.io!
Documentation is so bad it's simply not true.
I want to send a feedback to specific client over socket.io
My server side looks like this:
app.get('/upload', requiresLogin, function(request, response) {
response.render('upload/index.jade');
io.sockets.on('connection', function (socket) {
console.log('SOCKET ID ' + socket.id);
io.sockets.socket(socket.id).emit('new', 'hello');
});
});
and the client side looks like this:
$(document).ready(function() {
var socket = io.connect('http://localhost:80/socket.io/socket.io.js');
socket.on('new', function (data) {
console.log(socket.id);
console.log(data);
//$('#state').html(data.status);
});
});
but the client does simply nothing. I have tried nearly everything. Can someone tell me what I am doing wrong, please! :(
to send a message to a specific client save every one that connects to the server in an Object.
var socketio = require('socket.io');
var clients = {};
var io = socketio.listen(app);
io.sockets.on('connection', function (socket) {
clients[socket.id] = socket;
});
then you can later do something like this:
var socket = clients[sId];
socket.emit('show', {});
A couple of ways to send feedback to a specific client over socket.io include:
As stated by pkyeck, save all clients to an Object, so you can send to these specific clients later in your route handlers, e.g.:
var sessionsConnections = {};
sio.on('connection', function (socket) {
// do all the session stuff
sessionsConnections[socket.handshake.sessionID] = socket;
});
or, use socket.io's built in support for rooms - subscribe each client to a room on connection and send via this room within route handlers, e.g.:
sio.on('connection', function (socket) {
// do all the session stuff
socket.join(socket.handshake.sessionID);
// socket.io will leave the room upon disconnect
});
app.get('/', function (req, res) {
sio.sockets.in(req.sessionID).send('Man, good to see you back!');
});
Acknowledgement:
http://www.danielbaulig.de/socket-ioexpress/#comment-1158
Note that both these example solutions assume that socket.io and express have been configured to use the same session store and hence refer to the same session objects. See the links above and below for more details on achieving this:
https://github.com/LearnBoost/socket.io/wiki/Authorizing
2 things
1) You should really place your io.sockets.on(..) outside your app/update route to prevent adding multiple listeners for clients.
2) io.sockets.socket(id); should not be used, it should have been socket.emit('new', 'hello')
In socket.io 1.0, this is how it would work. It may work for lower versions, but I cannot guarantee it.
socket.to(socket_id_here).emit('new', 'hello');
This works because socket.io automatically adds a socket to a room with the socket's id on connection.
Also, if you plan to upgrade to version 1.0, there are a lot of changes to the api, so you'll sometimes have to change some code to make it work in 1.0.
The correct way to do this in Socket.io 1.0+ is:
io.to(users_socket_id).emit('new', 'hello');
You can also substitute a room name for 'users_socket_id' to emit to a specific socket.io room.
First of all, you cannot use socket.id on client side.
And then change the line
var socket = io.connect('http://localhost:80/socket.io/socket.io.js');
to
var socket = io.connect('http://localhost:80/');
I believe io.sockets.socket has been removed and has been a problem in Socket.IO (https://github.com/socketio/socket.io/issues/1618).
You can use io.sockets.connected[socket.id] and store the socket.id to reference with the user via username:
var usernames = {};
usernames[username] = socket.id;
// Sending the message
io.sockets.connected[usernames[username]].emit(...);
I don't see it anywhere on the documentation so I can see how this hasn't been answered. Also, if you don't have any reference via usernames, you can instead try:
users[socket.id] = socket.id;
to duplicate the key and value for easy access.
There is also possibly another way by using io.clients as described below, but I haven't used that method.
OTHER SOLUTION: Send message to specific client with socket.io and node.js
Have you tried using?
var socket = io.connect('http://localhost:80/');
i tired with the latest version of node and socket.io
below i am going to post complete code
<ul id="messages"></ul>
<form action="">
<input id="id1" /><button>Send</button>
</form>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
var io = io.connect();
$('form').submit(function(){
io.emit('drumevent', $('#id1').val());
$('#id1').val('');
return false;
});
io.on('drumevent', function(msg){
console.log(msg);
$('#messages').append($('<li></li>').text(msg.message+' quantity = '+msg.quantity));
});
</script>
server side code
var usernames = {};io.on('connection', function(socket){usernames["username"] = socket.id;
socket.on('drumevent', function(msg){
var socketId = socket.id;io.to(socketId).emit('drumevent', data);

Resources