I am having trouble adding users to a room list, I can manage to do it on a global level (every user connected), but adding and removing users on a room basis, I just can't seem to grasp.
I have added a new var called
roomUsers
app.js
var express = require('express')
, app = express()
, http = require('http')
, server = http.createServer(app)
, io = require('socket.io').listen(server);
server.listen(8080);
// routing
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
// usernames which are currently connected to the chat
var usernames = {};
// usernames which are currently connected to the chat
var roomUsers = {};
// 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');
// update the list of users in chat,client side
io.sockets.emit('updateUsers', usernames);
//
//io.sockets.in(socket.room).emit('updateUsers', usernames);
});
// 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){
socket.leave(socket.room);
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');
delete usernames[socket.username];
// 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);
io.sockets.emit('updateUsers', usernames);
});
// 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);
});
});
Is there any advice on where I am going wrong with io.sockets.emit('updateUsers', usernames); and delete usernames[socket.username]; (obviously I need to change that to roomUsers)
frontend.js
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 'updateusers', this updates the username list
socket.on('updateUsers', function(data) {
$('#users').empty();
$.each(data, function(key, value) {
$('#users').append('<div>' + key + '</div>');
});
});
// 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();
}
});
});
index.html
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script src="http://localhost:8888/chat/js/frontend.js"></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:100px;border-right:1px solid black;height:300px;padding:10px;overflow:scroll-y;">
<b>USERS</b>
<div id="users"></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>
You should define add/remove functions inside users.js under utils folder and then import it to the app.js.
users.js
let users = [];
const addUser = ({ id, username, room }) => {
// Clean the data
username = username.trim().toLowerCase();
room = room.trim().toLowerCase();
// always validate the data
if (!username || !room) {
return {
error: "Username and room are required!"
};
}
// Check for existing user
const userExist = users.find(user => {
return user.room === room && user.username === username;
});
// Validate username
if (userExist) {
return {
error: "Username is in use!"
};
}
// Store user
const user = { id, username, room };
users.push(user);
return { user };
};
const removeUser = id => {
const index = users.findIndex(user => {
user.id === id;
});
if (index !== 1) {
//splice method returns the removed the user
return users.splice(index, 1);
}
};
module.exports = { addUser, removeUser};
Users are added, when they submitted the login form and joined the room. And we need removedUser to send disconnected users who are in particular room.
app.js
const { addUser, removeUser} = require("./utils/users");
//a simple utility function that send text and date
const { sendMessage } = require("./utils/messages");
io.on("connection", socket => {
//once user submitted the form, server receives the username and room info. data={username,room}
socket.on("join", (data, callback) => {
//if you check addUser function it either returns error or user
const { error, user } = addUser({ id: socket.id, ...data });
if (error) {
return callback(error);
}
//if there is no error, user joins the room
socket.join(user.room);
//send this generated message to the connected users
socket.emit("message", sendMessage("Admin", "Welcome!"));
//user broadcast to send info all users in the room but the last joined
socket.broadcast
.to(user.room)
.emit(
"message",
sendMessage("Admin", `${user.username} has joined!`)
);
callback();
});
socket.on("disconnect", () => {
//socket.id is generated by socket upon connection
const user = removeUser(socket.id);
if (user) {
io.to(user.room).emit(
"message",
sendMessage("Admin", `${user.name} has left`)
);
}
});
/utils/messages.js
const generateMessage = function(username, text) {
return {
username,
text,
created: new Date().getTime()
};
};
module.exports = { generateMessage };
Related
I'm new to node
I have a PHP/Laravel cms and I have a simple Nodejs game server which is basically a loop generating some numbers
I connect my PHP backend to Nodejs via Socketio and use Socketio-JWT to identify the user
my client side (php/laravel)
PHP
$userToken = JWTAuth::customClaims(['userid' => Auth::user()->id, 'name' => Auth::user()->name, 'avatar' => asset_url('image/avatar-default.png')])->fromUser(Auth::user() );
html/js
var socket = io.connect("http://localhost:666");
socket.on('connect', function () {
socket.emit('authenticate', {token: '{{$userToken}}'});
});
socket.on('authenticated', function () {
console.log('Authenticated');
});
socket.on('unauthorized', function (data) {
console.log('Unauthorized, error msg: ' + data.message);
});
my serverside
const _online_users = { };
io.sockets
.on('connection', socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
}))
.on('authenticated', function(socket) {
_online_users[socket.decoded_token.userid] = {
name : socket.decoded_token.name ,
avatar : socket.decoded_token.avatar ,
}
io.sockets.emit('update_online_users' , _online_users );
socket.on('disconnect', function() {
console.log(`----- ##disconnect -----`)
});
}) ;
as you can see I have an object called _online_users and I store authenticated users in this object and then I send it to the clients so they know who is online
io.sockets.emit('update_online_users' , _online_users );
here is the problem, when the user gets disconnected
socket.on('disconnect', function() {
console.log(`----- ##disconnect -----`)
});
I have to update my _online_users object and remove disconnected user .... how should I go about this? I was thinking maybe I can store the token itself in the _online_users
_online_users[socket.decoded_token.userid] = {
token : socket.token ,
name : socket.decoded_token.name ,
avatar : socket.decoded_token.avatar ,
}
and when the user gets disconnected I get the disconnected token from the socket and remove the user from an object by that token
of course, this is all theory! I'm not sure if that's the way to go .... first of all, I can't access the token itself from the socket !
or let's say one of the users sends another request to node server, how can I identify the user sending the request?
.on('authenticated', function(socket) {
socket.on('somaction', function() {
console.log(` who is this guy ? `)
});
})
is there anything unique insocket.decoded_token that I can use as id? if so I can store it in the online users send it back and forth when the user requests something
basically I'm lost and appreciate any pointers
You can use a middleware now in the newer versions of socket-io. So you can check if the user is logged in by the jwt token that is sent with the request. If decoded successfully you can assign the user info to the current socket and call next() and go to the event you are listening for. Here is the example provided in the socket-io docs slightly modified for your case.
io.use(function(socket, next) {
const handshakeData = socket.request;
// make sure the handshake data looks good as before
// if error do this:
// next(new Error('not authorized'));
// else decode jwt token here and append the user to the socket.request
// and call next
// pseudo code here
const {
authorization
} = handshakeData.header
let token;
if (authorization && authorization.split(" ")[0] === "Bearer") {
token = authorization.split(" ")[1]
}
let user = jwt.decode(token, secret);
socket.request.user = user;
next();
});
You are doing well!
Since you are adding socket event handlers in the 'authenticated' handler you still have access to socket.decoded_token.userid.
This should be enough:
const _online_users = {};
io.sockets
.on('connection', socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
}))
.on('authenticated', function(socket) {
_online_users[socket.decoded_token.userid] = {
name: socket.decoded_token.name,
avatar: socket.decoded_token.avatar,
};
io.sockets.emit('update_online_users', _online_users);
socket.on('disconnect', function() {
console.log(`----- ##disconnect -----`);
delete _online_users[socket.decoded_token.userid];
io.sockets.emit('update_online_users', _online_users);
});
});
or to be a little more concise:
const _online_users = {};
io.sockets
.on('connection', socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
}))
.on('authenticated', function(socket) {
const { avatar, name, userid } = socket.decoded_token;
_online_users[userid] = { name, avatar };
io.sockets.emit('update_online_users', _online_users);
socket.on('disconnect', function() {
delete _online_users[userid];
io.sockets.emit('update_online_users', _online_users);
});
socket.on('any other event...', function() {
// ... still have access to userid
});
});
Edit: About unauthenticated socket I don't know; doc says nothing. You could try something like:
io.socket.on('connection', socket => {
socket.emit('update_online_users', _online_users);
// I'm afraid this closes the socket if unauthorized, you could check by yourself
socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
})(socket);
}).on('authenticated', socket => {
//...
});
Hope this helps.
when users online and don't close our clients such as browser tab or android application, i can send message to each specific user by
socket.broadcast.to(socketId)
.emit('new message', {
username: data.fromUsername,
money : 'Hurrraaa'
});
when users close clients as mobile application this event don't trigger but i can send any message to broadcast as:
socket.broadcast.emit('new message', "hooooorrrrraaaaa");
my users don't use client application any time, but i need to send message to some specific user and notify user until opening application and see message, users should be on'time in my application to get every message which i want to send from server like with Chat messengers which don't need users currently are using application such as WhatsApp, how can i resolve this problem?
then problem is send message to some specific users when they are istalled application and logged ti sever, but not using now and application waiting to receive message such as broadcast or special message to himself
this code is my simplified server:
var socket = require('socket.io'),
express = require('express'),
app = express(),
server = require('http').createServer(app),
io = socket.listen(server),
port = process.env.PORT || 3000,
mysql = require('mysql'),
uuid = require('node-uuid'),
datetime = require('node-datetime'),
moment = require('moment'),
bcrypt = require('bcrypt'),
async = require('async'),
request = require('request'),
redis = require("redis"),
redisClient = redis.createClient(),
forever = require('forever'),
log = require('log4node');
var io_redis = require('socket.io-redis');
io.adapter(io_redis({host: 'localhost', port: 6379}));
require('sticky-socket-cluster/replace-console')();
var options = {
workers : require('os').cpus().length,
first_port : 8000,
proxy_port : 3000,
session_hash: function (req, res) {
return req.connection.remoteAddress;
},
no_sockets: false
};
require('sticky-socket-cluster')(options, start);
function start(port) {
io.sockets.on('connection', function (socket) {
socket.on('new message', function (data) {
socket.broadcast.emit('new message', "hooooorrrrraaaaa");
});
socket.on('login', function (data) {
log.info(JSON.stringify(data))
login(data.username, data.password, function (success, value) {
if (success) {
redisClient.exists(data.username, function (err, doesExist) {
if (err) return;
if (!doesExist) {
redisClient.set(data.username, socket.id, function (err, res) {
redisClient.set(data.username, socket.id);
});
}
else {
redisClient.del(data.username);
redisClient.set(data.username, socket.id, function (err, res) {
redisClient.set(data.username, socket.id);
});
}
});
socket.emit('login', {
result : true,
id : value.id,
registeredMobileNumber: value.registeredMobileNumber
});
} else {
socket.emit('login', {result: false});
}
});
});
socket.on('userConnected', function (username) {
redisClient.exists(username, function (err, doesExist) {
if (err) return;
if (!doesExist) {
redisClient.set(username, socket.id, function (err, res) {
redisClient.set(username, socket.id);
});
}
else {
redisClient.del(username);
redisClient.set(username, socket.id, function (err, res) {
redisClient.set(username, socket.id);
});
}
});
});
socket.on('disconnectUser', function (data) {
redisClient.exists(data.username, function (err, doesExist) {
if (err) return;
if (doesExist) {
redisClient.del(data.username);
}
});
});
server.listen(port, function () {
console.log('Express and socket.io listening on port ' + port);
});
}
You can use socket.on('disconnect', function() {});
When a User disconnects , save the users user_id.
Subsequent message on the user_id would be saved in the server.
When the client reconnects again get the time of the latest message and then push the message after that time (saved in the server) to the client.
i have code for server
server.js
var socket = require( 'socket.io' );
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = socket.listen( server );
var port = process.env.PORT || 3000;
var nik = {};
server.listen(port, function () {
console.log('Server listening at port %d', port);
});
io.on('connection', function (socket) {
socket.on( 'new_count_message', function( data ) {
io.sockets.emit( 'new_count_message', {
new_count_message: data.new_count_message
});
});
socket.on( 'update_count_message', function( data ) {
io.sockets.emit( 'update_count_message', {
update_count_message: data.update_count_message
});
});
});
and this is how i use that
$.ajax({
type: "POST",
url: "(some_url)",
data: $("id_form").serialize(),
dataType: "json",
beforeSend:function(){
alert('bla..bla..');
},
success: function (result) {
if (result.status) {
var socket = io.connect('http://' + window.location.hostname + ':3000');
socket.emit('new_count_message', {
new_count_message: result.new_count_message
});
} else if (result.status == false) {
alert(error);
return false;
}
},
error: function(xhr, Status, error) {
alert(error);
}
});
that function is working perfectly, but it send to all. how to send notif to specific user? i have the ID user that i want to send the notif
Thanks
Well,
With io.sockets.emit you emit a message to all sockets. Instead use io.sockets.in("roomname").emit("message").
As well if you have the socket ID where you want to send the message you can use io.sockets.connected["socketid"].emit("message").
If you are inside the io.on('connection') function and you want to send a message to the same socket you can simply use socket.emit.
Another way is:
When a new socket connects, add this socket to a specific room socket.join("UniqueUserId") or socket.join("UniqueUserSessionId") ... Then use the 1st option io.sockets.in("UniqueUserId").emit("message") or io.sockets.in("UniqueUserSessionId").emit("message")
Examples:
io.on('connection', function (socket) {
//get the unique socket socketId on connection
var socketId = socket.id;
//you can add this socket id to a Database to use it later, etc...
//use sessionStore like Redis or memStore to get a unique sessionId
//as well you can extract a cookie with the UserId (you need to secure this to be sure that the user not modified the cookie) (you can use 2 cookies 1 for the userid other for the encrypted password and check if the cookies data is the same than in your users Database) etc etc etc. User Session is a lot better). Read about nodejs session store and socket session. Something like...
var cookies = qs.parse(socket.handshake.headers.cookie, "; ");
var user_id = cookies.user_id; //or some other cookie name;
socket.join(user_id);
socket.on( 'new_count_message', function( data ) {
//all sockets
io.sockets.emit( 'new_count_message', {
new_count_message: data.new_count_message
});
//same Socket
socket.emit( 'new_count_message', {
new_count_message: data.new_count_message
});
//specific Socket by SocketId
//io.sockets.connected["socketid"].emit( 'new_count_message', {
io.sockets.connected[socketId].emit( 'new_count_message', {
new_count_message: data.new_count_message
});
//all sockets in a specific Room
//io.sockets.in("roomname").emit( 'new_count_message', {
io.sockets.in(user_id).emit( 'new_count_message', {
new_count_message: data.new_count_message
});
});
});
I want to write a simple chat for practical experience.
All right, but I can't get a socket.nickname for notice a join/leave from the room. (when I tried pass its, he always sad a 'undefined').
Now all right, tried to create list of rooms
UPDATE CODE:
client.js:
$('#roomForm').submit(function() {
socket.emit('createRoom', $('#roomName').val());
$('#roomForm').hide();
$('#chatForm').show();
return false;
});
socket.on('message', function(data) {
newMessage(data);
});
socket.on('showRooms', function(rooms) {
console.log(rooms);
for(var i = 0; i < rooms.length; i++) {
$('#rooms').append($('<li>')
.append($('<form id="freeRoom">')
.append($('<span id="room">').text(rooms[i] + ' ///'))
.append($('<button>').text('connect'))));
};
});
$('#freeRoom').submit(function() {
socket.emit('connectToRoom', $('#room').text());
return false;
});
server.js:
io.on('connection', function(socket) {
socket.on('sendNickname', function(username) {
socket.username = username;
users.push(socket.username);
socket.emit('showRooms', rooms);
});
socket.on('disconnect', function() {
socket.broadcast.to(socket.room).emit('notice', socket.username + ' has left the room');
users.splice(users.indexOf(socket.username), 1);
socket.emit('showRooms', rooms);
});
socket.on('message', function(data) {
socket.broadcast.to(socket.room).emit('message', data);
});
socket.on('createRoom', function(room) {
socket.leave(socket.room);
socket.room = room;
rooms.push(socket.room);
socket.join(socket.room);
socket.emit('showRooms', rooms);
console.log('Rooms: ' + rooms);
socket.broadcast.to(socket.room).emit('notice', socket.username + ' has joined to room');
});
socket.on('connectToRoom', function(room) {
console.log('Will connect to that room: ' + room);
socket.join(room);
});
});
**UPD 2: **
Tried to connect free created room:
$('#freeRoom').submit(function() {
socket.emit('connectToRoom', $('#room').text());
return false;
});
P.S. And... Sorry for my english >.<
The event name that you emit, that is 'connect' is reserved in Socket.io along with 'message' and 'disconnect':
http://socket.io/docs/#sending-and-receiving-events
Socket.IO allows you to emit and receive custom events. Besides
connect, message and disconnect, you can emit custom events:
...
Change it to something else, e.g:
Server.js:
io.on('connection', function(socket) {
socket.on('send-nickname', function(nickname) {
socket.nickname = nickname;
users.push(socket.nickname);
console.log(users);
});
...
Client.js
socket.emit('send-nickname', nickname);
How do I emit a message to all users in a private chat sharing a conversation_id using node.js and socket.io?
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server);
conversations = {};
app.get('/', function(req, res) {
res.sendfile('/');
});
io.sockets.on('connection', function (socket) {
socket.on('send message', function (data) {
var conversation_id = data.conversation_id;
if (conversation_id in conversations) {
console.log (conversation_id + ' is already in the conversations object');
// emit the message [data.message] to all connected users in the conversation
} else {
socket.conversation_id = data;
conversations[socket.conversation_id] = socket;
conversations[conversation_id] = data.conversation_id;
console.log ('adding ' + conversation_id + ' to conversations.');
// emit the message [data.message] to all connected users in the conversation
}
})
});
server.listen(8080);
You have to create a room with conversation_id and make users to subscribe to that room, so that you can emit a private message to that room it by,
client
var socket = io.connect('http://ip:port');
socket.emit('subscribe', conversation_id);
socket.emit('send message', {
room: conversation_id,
message: "Some message"
});
socket.on('conversation private post', function(data) {
//display data.message
});
Server
socket.on('subscribe', function(room) {
console.log('joining room', room);
socket.join(room);
});
socket.on('send message', function(data) {
console.log('sending room post', data.room);
socket.broadcast.to(data.room).emit('conversation private post', {
message: data.message
});
});
Here is the docs and example for creating a room, subscribing to the room and Emit message to a room:
Socket.io Rooms
Socket.IO subscribe to multiple channels
Socket.io rooms difference between broadcast.to and sockets.in
SURE: Simply,
This is what you need :
io.to(socket.id).emit("event", data);
whenever a user joined to the server,socket details will be generated including ID.This is the ID really helps to send a message to particular people.
first we need to store all the socket.ids in array,
var people={};
people[name] = socket.id;
here name is the reciever name. Example:
people["ccccc"]=2387423cjhgfwerwer23;
So, now we can get that socket.id with the reciever name whenever we are sending message:
for this we need to know the recievername.You need to emit reciever name to the server.
final thing is:
socket.on('chat message', function(data){
io.to(people[data.reciever]).emit('chat message', data.msg);
});
Hope this works well for you.!!Good Luck