I'm trying to build a private messaging app using socket.io. My assumption was that per session there would only ever be one socket.id. If i keep track of my socket.id and console.log it, it's constantly chnaging and I dont know why.
Here a snippet if how I am saving a new user.
//Listen on the connection for incoming sockets
io.on('connection',function (socket) {
console.log(socket.id)
//Add the users to the socket session
socket.on("add-user", function(username){
oCurrentUser = {
username: username,
id: socket.id
};
console.log("socket.id for this user is", oCurrentUser.id);
aClients.push(oCurrentUser);
console.log(aClients);
socket.emit('login',oCurrentUser);
});
});
Response after I create a users:
listening on *:3000
Vws9v-Wegjx4bvBKAAAA
e611mmTgdYmFvhuMAAAB
IPE95tFpgem0eyvyAAAC
m5YLVR0PE_Qqc-AcAAAD
GXbyRVYAnHgBz4VzAAAE
When I do console.log(socket.id) is get like 5 ID's. I would of assumed it would return just one right?
Related
I have a chat app as real-time with socket.io. I want to save online/offline status on the database in Laravel back-end. For online status I don't have problem, but for offline status, I don't have any plan. My front-end framework is Angularjs 1.x and use JWT for authentication.
As matter of fact I want to save offline user status in database when user close browser of change route and emit all users from these user's status?
One possible solution would be just reacting to the socket.io server side events like the listening the socket connection and disconnect events.
This would the easiest solution, the problems would occur when your server crashes and you never receive the disconnect event. To avoid such problem, you could create interval check, which after every minute checks the opened sockets and users opened/closed in DB and updates if there changes.
Client side:
var socket = io("http://127.0.0.1:3000/", { query: "id=434354fds43" });
Server side:
var io = require('socket.io')(80);
var users = {};
io.use(function (socket, next) {
console.log('Query: ', socket.handshake.query);
var id = socket.handshake.query.id;
if (id) {
users[socket.id] = id;
return next();
}
// call next() with an Error if you need to reject the connection.
next(new Error('Authentication error'));
});
io.on('connection', function (socket) {
// New connection, get the ID from map
var id = socket.id;
socket.on('disconnect', function () {
// User left
delete users[id];
});
});
I've been reading answers about this problem for some time now but none of the solutions seem to work for my setup.
I have a nodeJS server in conjunction with express. I use Socket.io to send notifications to individual users. (frontend is Angular)
When a user logs in, he joins a room named after his email address (unique).
io.on('connection', function (socket) {
socket.on('join', function(user) {
//list of connected users
connected_users.push({socket_id: socket.id, email: user.email});
socket.join(user.email);
});
...
The join event is broadcasted from angular when a user logs in.
This way I can send notifications like so simply by using email addresses:
io.sockets.in(to).emit('notification', {
message: msg,
source: from,
destination: to,
event: data
});
When a user manually logs out I register the following event listener:
socket.on('leave', function(user) {
//remove the user from the list
var index = findUserConnected(socket.id);
if(index != null) {
connected_users.splice(index, 1);
}
socket.leave(user.email);
});
And finally there's the disconnect handler for whenever a user logs out or refreshes the page:
socket.on('disconnect', function() {
//if the user refreshes the page, he is still in the connected users list
var email = findEmailUserConnected(socket.id);
if(email != null) {
//we make him join back his room
socket.join(email);
}
});
Technically this works. On page refresh, the user joins back his room.
The problem is only on page refresh, notifications sent using io.sockets.in(email).emit('notification', {}); are not received even though the user is in his room.
Apparently a page refresh calls socket.disconnect() which generates a new socket_id. I'm not sure if there's a way to reassign a socket_id to a room or something similar.
Ok first of all receiving a 'disconnect' event on server means that connection on that socket is going to terminate. So, there is no use for making that same socket join back in a room as you are doing right now.
socket.on('disconnect', function() {
var email = findEmailUserConnected(socket.id);
if(email != null) {
socket.join(email); // this would never work because this socket connection is not going to exist anymore.
}
});
My suggestion would be to make sure that the user always joins back into the room(email) every time a new connection is made. It can be easily done by adding sending the join event on every new connection.
In your client side code do
var socket = io();
socket.on('connect', function() { // 'connect' event is received on client on every connection start.
socket.emit('join', user); // where 'user' is your object containing email.
})
This way it ensures that whenever a new connection is established the join event is sent to server and the 'socket.on('join',...)' listener in your server will add the new socket to the room. Hope this helps :)
I am building a social network application like facebook twitter. In this application one user have many followers. I need to apply socket.io when I give a status then It's only published on group of followers those who follow this specific user only. I've implemented socket.io in my application but now It's working all of the browser newsfeed but I want to apply socket only those who follow me.
I applied socket following way,
At client,
socket.emit('load-post', timelinePost);
socket.on('post-received', function(timelinePosts) {
$rootScope.timelinePosts = timelinePosts;
});
Server,
var io = require('socket.io')(server);
io.on('connection', function(socket) {
console.log('new connection made!');
// Broadcast the post
socket.on('load-post', function(data) {
socket.emit('post-received', data);
});
socket.on('disconnect', function() {
console.log('disconnected!');
});
});
News feed functions where I want to apply socket,
getNewsFeedServices.getStatusPosts().then(function(successMessage) {
$rootScope.posts = successMessage.data;
}, function(errorMessage) {
console.log(errorMessage);
});
This function load all the news feed informations of user followers and bring out all followers posts and then It's load individual accounts holder. But I want to apply here socket when user post a status then only followers get this status. How can I do this, any Ideas?
consider creating a Room for every online person, and then join the new and old active sockets (owned by followers) to that room.
I am not sure how to word this question right, but here I go. I have laravel, angular, node w/socket.io and I am also using JWT for authentication. My end goal is to be able to send real time update notifications to specific users. For the life of me, I cannot seem to get how the workflow would be.
My initial though was to send the jwt within the handshake and then use then in node to do http requests to get data, and then return said data. In another words, when a specific event is fired, node will already have the token, send request to laravel for specific information.
Can someone please explain to me how sending user specific data via socket.io in this architecture?
I found this great article : https://www.ukietech.com/blog/programming/step-by-step-instruction-of-setting-up-real-time-secure-broadcasting-with-laravel-5-1-socket-io-and-redis/
This set me on the right track.
First I need to pass in my JWT into the socket:
var socket = io('http://192.168.10.10:3000', {query: "Authorization="+$rootScope.$storage.satellizer_token});
Next I actually verify the token.. again. I know this may be overkill, but I want to know that what hits the socket is legit.
io.use(function(socket, next){
if (socket.handshake.query.Authorization) {
var config = {
url:'http://192.168.10.10/api/auth',
headers:{
Authorization:'Bearer '+socket.handshake.query.Authorization
}
};
request.get(config,function(error,response,body){
socket.userId = JSON.parse(body).id;
next();
});
}
// call next() with an Error if you need to reject the connection.
next(new Error('Authentication error'));
});
The request in this block of code returns a user object based on the authenticated token. Refer to JWTAuth for more.
Then on connection I will assign the user to a unique channel.
io.on('connection',function(socket){
socket.join('userNotifications.'+socket.userId);
console.log('user joined room: userNotifications.'+socket.userId);
});
Then broadcast the event:
notifications.on('pmessage', function(subscribed, channel, message) {
var m = JSON.parse(message);
io.emit(channel+":"+m.event, message);
});
Back on the client side I listen for the channel. the var user is the user id.
socket.on('userNotifications.'+ user+':App\\Events\\notifications', function(message){
console.log(message);
});
Situation
I'm using the library SocketIO in my MEAN.JS application.
in NodeJS server controller:
var socketio = req.app.get('socketio');
socketio.sockets.emit('article.created.'+req.user._id, data);
in AngularJS client controller:
//Creating listener
Socket.on('article.created.'+Authentication.user._id, callback);
//Destroy Listener
$scope.$on('$destroy',function(){
Socket.removeListener('article.created.'+Authentication.user._id, callback);
});
Okey. Works well...
Problem
If a person (hacker or another) get the id of the user, he can create in another application a listener in the same channel and he can watch all the data that is sends to the user; for example all the notificacions...
How can I do the same thing but with more security?
Thanks!
Some time ago I stumbled upon the very same issue. Here's my solution (with minor modifications - used in production).
We will use Socket.IO namespaces to create private room for each user. Then we can emit messages (server-side) to specific rooms. In our case - only so specific user can receive them.
But to create private room for each connected user, we have to verify their identify first. We'll use simple piece of authentication middleware for that, supported by Socket.IO since its 1.0 release.
1. Authentication middleware
Since its 1.0 release, Socket.IO supports middleware. We'll use it to:
Verify connecting user identify, using JSON Web Token (see jwt-simple) he sent us as query parameter. (Note that this is just an example, there are many other ways to do this.)
Save his user id (read from the token) within socket.io connection instance, for later usage (in step 2).
Server-side code example:
var io = socketio.listen(server); // initialize the listener
io.use(function(socket, next) {
var handshake = socket.request;
var decoded;
try {
decoded = jwt.decode(handshake.query().accessToken, tokenSecret);
} catch (err) {
console.error(err);
next(new Error('Invalid token!'));
}
if (decoded) {
// everything went fine - save userId as property of given connection instance
socket.userId = decoded.userId; // save user id we just got from the token, to be used later
next();
} else {
// invalid token - terminate the connection
next(new Error('Invalid token!'));
}
});
Here's example on how to provide token when initializing the connection, client-side:
socket = io("http://stackoverflow.com/", {
query: 'accessToken=' + accessToken
});
2. Namespacing
Socket.io namespaces provide us with ability to create private room for each connected user. Then we can emit messages into specific room (so only users within it will receive them, as opposed to every connected client).
In previous step we made sure that:
Only authenticated users can connect to our Socket.IO interface.
For each connected client, we saved user id as property of socket.io connection instance (socket.userId).
All that's left to do is joining proper room upon each connection, with name equal to user id of freshly connected client.
io.on('connection', function(socket){
socket.join(socket.userId); // "userId" saved during authentication
// ...
});
Now, we can emit targeted messages that only this user will receive:
io.in(req.user._id).emit('article.created', data); // we can safely drop req.user._id from event name itself