emitting a message to node.js after page close - javascript

So I'm pretty new to it, so I apologize if I this is an entirely wrong approach of doing this, but I am currently programming a little chat application using node.js with express and socket.io. I managed to get a login message logged to the console but I am really struggling with the logout one. When a user opens up the page I take the roomname aswell as the username out of a cookie and send it to the server like shown here:
var socket = io();
var roomname = readCookie('Roomname');
var nickname = readCookie('Nickname');
var data = {'type': 'user', 'channel': roomname, 'user': nickname, 'data': "joined"};
socket.emit('chat', data);
After that I filter the message on the server side and send the answer to all clients like this:
case 'user':
io.emit('chat/' + msg.channel, {type: 'user', user: msg.user, data: msg.data});
break;
I always send the string "chat" followed by the roomname as a channel name so only the users that are in the right rooms can see the message. After that I sort out what kind of message was received by the client side:
case 'user':
console.log(msg.user + " just " + msg.data + " the room!");
break;
Now the only thing I need to do is somehow getting the room- and username to the server side once the user closes the page so I can send the message but with "left". (like this)
var data = {'type': 'user', 'channel': roomname, 'user': nickname, 'data': "left"};
socket.emit('chat', data);
My approach was finding an event that fires the code at the right time and I have been looking through stack overflow for a good hour now and I just couldn't find anything that did the job. I've tryed out all the variations of window.onbeforeunload I could find and none of them seemed to work. I could log some stuff to the console with some of them but I was never able to emit a message to the server side. I am aware that this may not be possible in the way I would like it to, but I really ran out of ideas what to try next. Can someone help me out?

You can add information about the client's username to the socket object, when the client connects for the first time. Then you will be able to use socket.username when client disconnects.
Client:
var socket = io();
var roomname = readCookie('Roomname');
var nickname = readCookie('Nickname');
socket.on('connect', () => {
socket.emit("hello", {nick:nickname, room:roomname});
});
Server:
socket.on("hello", (data) => {
socket.username = data.nick;
socket.join(data.room);
socket.room = data.room;
});
socket.on("disconnect", () => {
var data = {'type':'user', 'channel':socket.room, 'user':socket.username, 'data':'left'};
socket.broadcast.to(socket.room).emit('chat', data};
socket.leave(socket.room);
});

Related

Sending and getting data with socket io for two instructions

I'm trying to find a way to emit from my client an instruction to the server which is inside a JSON object.
Here's my problem, my server receive my first instruction. But my second instruction 'deleteFile' is inside a json object,and the server never received this second instruction.
I would like to know if this is possible, and if i'm doing it in the wrong way.
I want to do something like this:
Client: I emit 'instruction' with my var "message"
service.deleteFile = function (peer, filename, callback) {
if (! LoginService.connected || ! LoginService.socket) {
console.log("deleteFile : not connected to server");
callback("no server");
var message = {
message : 'deleteFile',
dest_list : _.flattenDeep([peer]),
filename : filename,
};
LoginService.socket.emit('instruction',(message));
console.log("service.deleteFile : " , message);
callback(200);
};
And on server app.js for 'instruction':
socket.on('instruction', function(jsonMessage){
var dest_list = jsonMessage.dest_list;
var message = jsonMessage.message;
var filename = jsonMessage.filename;
var user_id = dest_list;
var instruction = {
message : message,
user_id : user_id,
filename : filename,
};
if (dest_list.length){
for (var i = 0; i < dest_list.length; i++) {
var user_id = dest_list[i].toLowerCase();
if (user_id in socket_clients){
var socketId = socket_clients[user_id].socketId;
socket.broadcast.to(socketId).emit('instruction', instruction);
console.log(instruction); //print "{message:'deleteFile', user_id: ['emitter'], filename: 'thegoodfile'}
}
else{
console.log("Error", user_id);
}
}
} else{
console.log("Error");
} });
Then on server app.js for 'deleteFile'(this instruction is inside my JSON object emited from client):
socket.on('deleteFile', function(jsonMessage) {
console.log("Test message"); };
I think my server don't understand my instruction 'deleteFile', but I don't find a way to tell him that it is an instruction.
Tell me if I missed some informations.
Thank you if you can help.
Found a solution with this post: socket, emit event to server from server
I can't send from my server to himself with 'broadcast'. From socket.io doc:
// sending to all clients except sender
socket.broadcast.emit('broadcast', 'hello friends!');
It was written ..
So I used Event handler in Node (doc: https://nodejs.org/api/events.html) and it works.

nodejs and undefined variable, possible scope issue

I have started learning Node JS today and have made a chatroom.
When a user connects, it sends their user information to Node and that will store that in a var called user which is created in the anonymous function of io.on('connecting', function ... so I can have multiples of them for multiple users (I think that would work)
When the user disconnects, I remove an object of their user from usersOnline which is indexed by their user.id
I keep getting this error that tells me it is undefined:
Error TypeError: Cannot read property 'id' of undefined
at Socket.<anonymous> (/var/www/html/projects/hbc_chat/index.js:28:27)
The error is where I use delete usersOnline[user.id]
And here is the code:
// server user
var srvUser = {
id: 0,
display_name: 'HabboCreate',
fancy_display_name: 'HabboCreate',
picture: 'http://www.habbocreate.com/userdata/uploads/2f48a19f199bb5f018b3089fd4967902'
};
var usersOnline = {};
io.on('connection', function(socket) {
var user;
socket.on('connected', function(userObj) {
// store user
user = userObj;
// add to users online list
usersOnline[user.id] = user;
// send to clients
updateUsersOnline();
// send joined message
sendMessage(srvUser, user.display_name + ' joined the chat');
console.log(user.display_name + ' connected');
});
socket.on('disconnect', function() {
try {
// remove their user from the online list
delete usersOnline[user.id];
// send to client
updateUsersOnline();
// send left message
sendMessage(srvUser, user.display_name + ' left the chat');
}
catch (err) {
console.log('Error', err);
}
});
....
This does not seem to be a scope issue. Is it possible updateUsersOnline() changes the user info? I just ran this exact code, very similar to yours, without errors:
var usersOnline = {};
io.on('connection', function (socket) {
var user;
socket.on('connected', function (userObject) {
user = userObject;
console.log('user: ' + JSON.stringify(user));
usersOnline[user.id] = user;
console.log(JSON.stringify(usersOnline) + '\n');
});
socket.on('disconnect', function () {
try {
console.log('user: ' + JSON.stringify(user));
delete usersOnline[user.id];
console.log(JSON.stringify(usersOnline) + '\n');
} catch (err) {
console.log('err: ' + err);
}
});
});
I passed a user created like this in the client 'connected' event:
var userObject = {
id: new Date(),
display_name: 'some_name',
};
my_connection.emit('connected', userObject);
After the connected event I closed the connection. This is what the server logged:
user: {"id":"2016-12-17T04:49:05.123Z","display_name":"some_name"}
{"2016-12-17T04:49:05.123Z":{"id":"2016-12-17T04:49:05.123Z","display_name":"some_name"}}
user: {"id":"2016-12-17T04:49:05.123Z","display_name":"some_name"}
{}
No errors, so it seems something is removing the user object or at least its id property in between your connected and disconnect events.
right, well I've figured out what was wrong.
It was where I was on the chatroom page, I would restart the node server, my JavaScript wouldnt emit the user details (It only done so upon load), so when i went to refresh or whatever, disconnect event wouldn't be able to find info in the user variable and error
My fix was to make a request user event client side and then emit that on the server when there's a new connection, as to not rely on the page load sending the details

Using rooms with socket.io

In the socket.io documentation I see an example of rooms
io.on('connection', function(socket){
socket.on('say to someone', function(id, msg){
socket.broadcast.to(id).emit('my message', msg);
});
});
I have a route /rooms/:roomId.
Is it possible to make the sockets being sent between the server and the client only hits the specific room?
I guess the server should be something like
io.on('connection', function(socket){
socket.on('new msg from client', function(roomId, msg){
io.to(id).emit('new msg from server', msg);
});
});
above and the client should send messages with
socket.emit('new msg from client', roomId, msg);
and get new messages simply with
socket.on('new msg from server', function () {
document.getElementById('msgs').appendChild(...);
});
But will this work? Shouldn't I join the room with socket.join(...) before I can do this?
For a haiku sharing application I made I have something like this:
io.on('connection', function(socket) {
var socket_id = socket.id;
var client_ip = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address.address;
clients.push(socket);
console.info('New client connected (id=' + socket.id + ').');
number_of_clients_connected++;
console.log('[' + client_ip + '] connected, ' + number_of_clients_connected + ' total users online.');
//when a socket is disconnected or closed, .on('disconnect') is fired
socket.on('disconnect', function() {
number_of_clients_connected--;
console.log('[' + client_ip + '] disconnected, ' + number_of_clients_connected + ' total users online.');
//on disconnect, remove from clients array
var index = clients.indexOf(socket);
if (index != -1) {
clients.splice(index, 1);
//console.info('Client gone (id=' + socket.id + ').');
}
});
So it keeps an array of clients and when certain messages need relaying you can specify the client socket ID...
//reads from latest_haikus_cache and sends them
socket.on('request_haiku_cache', function() {
latest_haikus_cache.forEach(function(a_latest_haiku) {
clients[clients.indexOf(socket)].emit('load_haiku_from_cache', a_latest_haiku);
});
});
The server is allowed to broadcast to any room so you can support letting a client ask the server to send to a room without that client being in the room. It really depends upon what you want to do.
So, if you want your server to have this code that lets any client send a message to any room of their choosing:
io.on('connection', function(socket){
socket.on('new msg from client', function(roomId, msg){
io.to(roomId).emit('new msg from server', msg);
});
});
Then, you can indeed do that and it will work. Whether it's appropriate or not is entirely up to your application and whether you want any client to be able broadcast to any room that it has the name of.
But will this work?
Yes, it will work.
Shouldn't I join the room with socket.join(...) before I can do this?
There is no need to have the client join the room unless it wants to receive messages for that room. You don't have be in the room in order to ask the server to send to that room if that's how you want to do it. So, all this is entirely up to your application and what is appropriate.
I have a route /rooms/:roomId.
Is it possible to make the sockets being sent between the server and
the client only hits the specific room?
I could not figure out what this part of your question means. If you care to explain further, I will try to help with this part too.

How to push notification to specific user Node + AngularJS + Laravel

I need help how to push notification to specific user. I can now push notifcation but all user will get that notification. I can filter it on clinet side but I think it is unsecure...
First I send data with laravel 5:
$redis = Redis::connection();
$redis->publish('update.answer', json_encode($events));
here is my node.js i emite data:
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('update.group', function(err, count) {
});
redis.subscribe('update.question', function(err, count) {
});
redis.subscribe('update.answer', function(err, count) {
});
redis.subscribe('update.comment', function(err, count) {
});
redis.on('message', function(channel, message) {
message = JSON.parse(message);
console.log(message);
io.emit(channel, message);
});
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
and with angularjs I take data and push to the client.
socket.on('update.answer',function(data){
if($scope.remove){
$scope.remove = false;
}
$scope.feed = $("#feed").val();
if(parseInt($scope.feed) === parseInt(data.userID)){
$scope.answers.push(data);
$scope.$digest();
}
});
WIth this part:
$scope.feed = $("#feed").val();
if(parseInt($scope.feed) === parseInt(data.user_id) && data.admin_id !== null){
}
I check if client should get notification but it is unsecure...
Any way to improve this?
To push message to specific user , you must store his/her reference somewhere.
for ex
io.on('connection', function(socket) {
socket.on('add-user', function(data){
clients[data.username] = socket;
});
});
now to push message to specific user just use his username to retrive his socket
clients[data.username].emit(channel, message);
Update : Explanation
This Assume that each user who uses you web app is having some sort of authentication.
As soon as user login into your application , let him join on the nodejs backend socket.
on client side
socket.emit('add-user',userObj);
});
userObj is object that contains user details,you can send the username alone too
socket.emit('add-user',username);
in your nodejs first decalre one array that contains the socket of all the users who joins the website
var clients = [];
now in your nodejs application write this additional code
io.on('connection', function(socket) {
socket.on('add-user', function(data){
clients[data.username] = socket;
});
});
up to this moment the user who login into your website will call add-user event from client side which will in turn call add-user on nodejs and there socket will be added into the clients array
now to send message to any particular user you must know there username,so if you know the username of the user then you can simply emit message to them using
clients[data.username].emit(channel, message);

How to send message to room clients when a client disconnects

I want the server to send a message to all room clients when one of them disconnects.
Something like this:
socket.on('disconnect', function() {
server.sockets.in(room).emit('bye');
});
But...
How do I know which room to broadcast?
What if the client has joined to multiple rooms?
After inspecting the sockets object, I came up with this solution:
socket.on('disconnect', function() {
var rooms = io.sockets.manager.roomClients[socket.id];
for (var room in rooms) {
if (room.length > 0) { // if not the global room ''
room = room.substr(1); // remove leading '/'
console.log('user exits: '+room);
server.sockets.in(room).emit('bye');
}
}
});
not 100% on this - but give it a try:
when connecting to a room or adding a new user to the mix, remember their username or id or something in the socket:
socket.on('adduser', function(username){
socket.username = username;
socket.join('room');
}
Then listen to leave events on rooms:
socket.room.on('leave', function(){
socket.broadcast.to(this).emit(socket.username + ' says seeya!');
}
It's worth a try anyway - I'm sure something similar will work if this doesn't.

Categories