Socket.io Identify User for Socket - javascript

I write some code example that identifi connected users via socket.io... So now I must write a code on index page to comunicate with users.
The code is below and HOW to send a message to user[1] "Welcome" and for user[2] "HI men" and also limit connection fr 2 users. so when 2 user connected then anybody else cant connect..
Index.html:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
var users;
socket.on('hello', function (data) {
console.log(data.hello);
});
socket.on('listing', function (data) {
users = data;
});
socket.on('chat', function (message) {
console.log(message);
});
socket.on('message', function (message) {
console.log(message);
});
function chat (message) {
socket.emit('chat', message);
}
function message (user, message) {
socket.emit('message', {
user: user,
message: message
});
}
</script>
app.js
var express = require('express');
var app = express.createServer();
var io = require('socket.io').listen(app);
app.listen(3000);
app.use(express.static(__dirname));
var users = {};
var userNumber = 1;
function getUsers () {
var userNames = [];
for(var name in users) {
if(users[name]) {
userNames.push(name);
}
}
return userNames;
}
io.sockets.on('connection', function (socket) {
var myNumber = userNumber++;
var myName = 'user#' + myNumber;
users[myName] = socket;
socket.emit('hello', { hello: myName });
io.sockets.emit('listing', getUsers());
socket.on('chat', function (message) {
io.sockets.emit('chat', myName + ': ' + message);
});
socket.on('message', function (data) {
users[data.user].emit('message', myName + '-> ' + data.message);
});
socket.on('disconnect', function () {
users[myName] = null;
io.sockets.emit('listing', getUsers());
});
});
app.listen(process.env.PORT);

You can start by taking a look at how to configure authorization with Socket.io. The handshakeData provided by the callback can be modified there (ie: add a username property), and any changes will be accessible via socket.handshake in your app.js (via the object passed in to the callback for io.sockets.on('connection',..). Using request header information that's also accessible from the handshakeData, you can set user values within the authorization callback (ie: from a database) so you can identify the user for the given socket in your app.js.
Here's a similar example

I know it has been a long time since you asked this, but just 4 days ago I published a module for node js, express and socket.io which manages that exactly thing you wanted. Check the Usage and Example; I hope you will find this module helpful!
You can install it via NPM socket.io.users This is a node js module for socket.io applications. One user per client.
Some of the usage code:
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var socketUsers = require('socket.io.users');
// ...
socketUsers.Session(app); // IMPORTANT !
// ...
var rootIo = require('socket.io')(server); // default '/' as namespace.
var chatIo = rootIo.of('/chat');
var rootUsers = socketUsers.Users; /* default '/' as namespace.
Each namespace has ITS OWN users object list,
but the Id of a user of any other namespace may
have the same value if request comes from the same client-machine-user.
This makes easy to keep a kind of
synchronization between all users of all the different namespaces. */
var chatUsers = socketUsers.Users.of('/chat'); //
rootIo.use(socketUsers.Middleware());
/* IMPORTANT but no errors if you want
to skip it for a io.of(namespace)
that you don't want the socket.io.users' support. */
chatUsers.use(socketUsers.Middleware());
chatUsers.on('connected',function(user){
console.log(user.id + ' has connected to the CHAT');
user.store.username = 'username setted by server side'; /*at the store
property you can store any type of properties
and objects you want to share between your user's sockets. */
user.socket.on('any event', function(data){
/*user.socket is the current socket, to get all connected sockets from this
user, use: user.sockets */
});
chatIo.emit('set username',user.store.username);
});
rootUsers.on('connected',function(user){
console.log('User has connected with ID: '+ user.id);
});
rootUsers.on('connection',function(user){
console.log('Socket ID: '+user.socket.id+' is user with ID: '+user.id);
});
rootUsers.on('disconnected',function(user){
console.log('User with ID: '+user.id+'is gone away :(');
});
//...server.listen blabla..

Related

Cannot connect or emit to Amazon ECS Socket.io Server via Socket.io from a HTML file located in S3 bucket

I am having trouble connecting and emitting to an ECS Fargate Socket.io Server on AWS via Socket.io from a HTML file located in a S3 bucket in the same AWS region.
The ECS Fargate Server's code is as follows:
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
var AWS = require ('aws-sdk');
io.on('connect', function (socket){
console.log('connection made');
socket.on('disconnect', function (socketds) {
console.log("***********************************************");
console.log("Disconnected");
});
socket.on('reconnect', function (socketds) {
console.log("***********************************************");
console.log("Reconnected");
});
socket.on('c_status', function () {
console.log("*********************************************");
console.log("cstatus");
});
socket.on('app_message', function (mdata) {
console.log("***********************************************");
console.log("App-message:",mdata);
socket.emit('command1',mdata);
});
});
http.listen(3000, function () {
console.log('listening on *:3000');
});
And the relevant code of the HTML file is as follows:
<script>
const serverLocation = "http://application-load-balancer-dns-link:3000";
const socket = io(serverLocation);
function sendmsg() {
var date = new Date();
date.setHours(date.getHours() + 4);
var datetm = date.toISOString().split('.')[0]+"Z";
var node_id = $('#innode').val();
var clid = "0";
var msg = $('#message').val();
if (node_id.length === 1) {
var unitcode = "0" + "0" + node_id;
}
else if (node_id.length === 2) {
var unitcode = "0" + node_id;
}
else {
var unitcode = node_id;
}
var mdata = datetm+unitcode+msg;
socket.emit('app_message',mdata);
}
</script>
I have been at it for 2 days now and have not been able to figure it out. It just doesn't connect.
Any help is greatly appreciated.
If you need more information, please let me know.
1) Try checking the inbound rules of the security group associated with that instance.
2) Outbound rules should also be configured as its bidirectional (By Default allows all)
3) If they are perfect. Check whether timeout occurs or any other exception.

How do I close a sockjs connection on the server side?

So, every time I refresh the page, it seems like sockjs is creating a new connection.
I am saving every message to my mongodb on every channel.onmessage, so if I refresh my page 7 times and send a message, I would save 7 messages of the same content into my mongodb.
This is very problematic because when I retrieve those messages when I go into the chat room, to see the log, I would see bunch of duplicate messages.
I want to keep track of all connections that are 'active', and if a user tries to make another connections, I want to be able to force close on the old one from the server side, so there is only 1 connection listening to each message at a time
How do I do this ?
var connections = {};
//creating the sockjs server
var chat = sockjs.createServer();
//installing handlers for sockjs server instance, with the same url as client
chat.installHandlers(server, {prefix:'/chat/private'});
var multiplexer = new multiplexServer.MultiplexServer(chat);
var configChannel = function (channelId, userId, userName){
var channel = multiplexer.registerChannel(channelId);
channel.on('connection', function (conn) {
// console.log('connection');
console.log(connections);
connections[channelId] = connections[channelId] || {};
if (connections[channelId][userId]) {
//want to close the extra connection
} else {
connections[channelId][userId] = conn;
}
// }
// if (channels[channelId][userId]) {
// conn = channels[channelId][userId];
// } else {
// channels[channelId][userId] = conn;
// }
// console.log('accessing channel! ', channels[channelId]);
conn.on('new user', function (data, message) {
console.log('new user! ', data, message);
});
// var number = connections.length;
conn.on('data', function(message) {
var messageObj = JSON.parse(message);
handler.saveMessage(messageObj.channelId, messageObj.user, messageObj.message);
console.log('received the message, ', messageObj.message);
conn.write(JSON.stringify({channelId: messageObj.channelId, user: messageObj.user, message: messageObj.message }));
});
conn.on('close', function() {
conn.write(userName + ' has disconnected');
});
});
return channel;
};
Simply use .close:
if (connections[channelId][userId]) {
// want to close the extra connection
connections[channelId][userId].close();
} else {
connections[channelId][userId] = conn;
}

Using Node.js to retrieve data from Redis through an AJAX request

I'm going through a Node, Express, & Socket.io chat tutorial. I decided to use Redis to store the chat history and have successfully set it up so that my information is correctly posting to the database. I am now trying to access that information to use on the client-side (in this case I'm trying to access the list of users currently in the chat so I can show them to the side of the chat). I am using $.getJSON to make a GET request. Right now I have it setup so that the file it tries to access only has this JSON object : {"dog" : "2","cat":"3"} just to test it, and that is working, but I'm not sure where to go from there because anytime I try adding a function into that file, even if I specify to return a JSON object and call that function, the request stops returning the correct information.
For example I tried :
var data = function(){
return {"dog" : "2","cat":"3"}
}
data();
and that doesn't return anything ( I understand that when I make a GET request the function isn't run, but it doesn't even return that text, and if it doesn't run a function than I'm not sure how I can access redis from this file)
Here's what I'm thinking:
var redis = require('redis')
//figure out how to access the redis client that I have at localhost:6379, something like var db = redis.X
//and then call (for example) db.smembers('onlineUsers') and be returned the object which I can iterate through
Here's my relevant code:
server.js:
var jade = require('jade');
var PORT = 8080;
var redis = require('redis');
var db = redis.createClient();
var pub = redis.createClient();
var sub = redis.createClient();
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);
server.listen(PORT, function(){
console.log("Now connected on localhost:" + PORT)
});
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.set("view options", {layout: false});
app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res){
res.render('home');
});
io.sockets.on('connection', function(client){
sub.subscribe("chatting");
sub.on("message", function (channel, message) {
console.log("message received on server from publish");
client.send(message);
});
client.on("sendMessage", function(msg) {
pub.publish("chatting",msg);
});
client.on("setUsername", function(user){
pub.publish("chatting","A new user in connected:" + user);
db.sadd("onlineUsers",user);
}
);
client.on('disconnect', function () {
sub.quit();
pub.publish("chatting","User is disconnected :" + client.id);
});
});
script.js:
$(document).ready( function(){
$client = io.connect();
initialize();
});
var setUsername = function(){
var username = $("#usernameInput").val();
if (username)
{
var user = username;
$client.emit('setUsername', username);
$('#chatControls').show();
$('#usernameInput').hide();
$('#usernameSet').hide();
showCurrentUsers();
}
}
var showCurrentUsers = function(){
$('#list_of_users').empty();
$.getJSON('getusers.js', function(data){
for (var i = 0; i < data.length; i++){
$('list_of_users').append("<li>"+data[i]+"</li>")
}
})
}
var sendMessage = function(){
var msg = $('#messageInput').val();
var username = $("#usernameInput").val();
if (msg)
{
var data = {msg: msg, user: username}
$client.emit('message', data);
addMessage(data);
$('#messageInput').val('');
// populate(username,msg);
}
}
var addMessage = function(data) {
$("#chatEntries").append('<div class="message"><p>' + data.user + ' : ' + data.msg + '</p></div>');
}
// var populate = function(username,msg) {
// var data ;
// }
var initialize = function(){
$("#chatControls").hide();
$("#usernameSet").on('click', setUsername);
$("#submit").on('click',sendMessage);
showCurrentUsers();
}
and right now all that the getusers.js file has in it is:
{"dog" : "2","cat":"3"}
It looks like you're expecting your call to $.getJSON to load and execute the javascript it loads. It doesn't work this way. You need to make a node endpoint (via a route) which renders the JSON. The node endpoint would then do the data manipulation / querying redis:
Node:
In routes.js:
app.get('/chatdata', ChatController.getChatData);
In ChatController.js (manipulate, create the data as you like here)
exports.getChatData = function (req, res) {
var data = function(){
return {"dog" : "2","cat":"3"}
};
res.JSON(data);
};
Front-end
$.getJSON('getChatData', function(data){
//...
})
I think you need to setup a route to handle the GET request that $.getJSON makes, or if getusers.js is in the /public directory, then you need to modify your $.getJSON call as follows:
$.getJSON('http://localhost:8080/public/getusers.js', function(data){
Ok, it looks like it is a problem with your getusers.js file. $.getJSON seems to prefer double quotes. Try formatting it like this:
{
"dog" : "2",
"cat" : "3"
}
Also, try using this to display the data:
$.getJSON('getusers.js', function(data){
var items = [];
$.each( data, function( key, val ) {
items.push("<li id='" + key + "'>" + val +"</li>");
});
$('#list_of_users').append(items.join(""));
});

Connection Pool for NodeJS

I have an app that has been maxing out the number of connection to MongoDB and I was under the assumption that if the drivers were set up correctly you didn't need to worry about closing connections.
I've seen people mention the Generic Pool module but what is the best process for closing or pooling connections using Node & MongoDB?
Here is my connection code for the app:
var sys = require("sys");
var app = require('http').createServer(handler);
var io = require('socket.io').listen(app);
app.listen(1337);
io.configure(function () {
io.set('authorization', function (handshakeData, callback) {
callback(null, true);
});
});
function handler (req, res, data) {
sys.puts('request made to trackerapp.js');
res.writeHead(200);
res.end(data);
}
io.sockets.on('connection', function (socket) {
socket.on('adTracker', function (data) {
var adRequestData = data;
var databaseUrl = "mongodb://dbuser:dbpass#mongolab.com/tracker";
var collections = ["cmnads"]
var db = require("mongojs").connect(databaseUrl, collections);
db.cmnads.insert({adRequest : adRequestData},function(err, updated) {
if( err || !updated ) console.log("mongo not updated" + err);
else console.log("data stored");
});
});
});
After seeing JohnnyHK's comment I was able to pull the connection event out of the Socket.io connection and it worked fine, see the solution below:
var databaseUrl = "mongodb://dbuser:dbpass#mongolab.com/tracker";
var collections = ["cmnads"];
var db = mongojs.connect(databaseUrl, collections);
io.sockets.on('connection', function (socket) {
socket.on('adTracker', function (data) {
var adRequestData = data;
//vars for MongoDB used to be created here... so new connect function was called on every request to socket.io
db.cmnads.insert({adRequest : adRequestData},function(err, updated) {
if( err || !updated ) console.log("mongo not updated" + err);
else console.log("data stored");
});
});
});
A technique I used with my express apps that seems have some measure of success is to open a connection to a mongo instance (thereby getting a connection pool) then sharing that db (that is now in the "connected" state) instance wherever it is needed. Something like this:
server = new Server(app.settings.dbsettings.host, app.settings.dbsettings.port, {auto_reconnect: true, poolSize: 5})
db = new Db(app.settings.dbsettings.db, server, {native_parser:false})
db.open(function(err, db) {
app.db = db;
server = app.listen(app.settings.port);
console.log("Express server listening on port %d in %s mode", app.settings.port, app.settings.env);
require('./apps/socket-io')(app, server);
});
This connects to the database at the highest level in my app before the program moves into the wait listen state.
Before I used this pattern I would create a new database object whenever I needed to interact with the database. The problem I found is that the new database object would create a new thread pool, consuming a bunch of ports. These were never cleaned up properly. After a period of time the machine that hosted the app would run out of ports!
Anyway, a variation on the code I have shown should be where you should do your thinking I believe.

Socket.io custom client ID

I'm making a chat app with socket.io, and I'd like to use my custom client id, instead of the default ones (8411473621394412707, 1120516437992682114). Is there any ways of sending the custom identifier when connecting or just using something to track a custom name for each ID? Thanks!
You can create an array on the server, and store custom objects on it. For example, you could store the id created by Socket.io and a custom ID sent by each client to the server:
var util = require("util"),
io = require('/socket.io').listen(8080),
fs = require('fs'),
os = require('os'),
url = require('url');
var clients =[];
io.sockets.on('connection', function (socket) {
socket.on('storeClientInfo', function (data) {
var clientInfo = new Object();
clientInfo.customId = data.customId;
clientInfo.clientId = socket.id;
clients.push(clientInfo);
});
socket.on('disconnect', function (data) {
for( var i=0, len=clients.length; i<len; ++i ){
var c = clients[i];
if(c.clientId == socket.id){
clients.splice(i,1);
break;
}
}
});
});
in this example, you need to call storeClientInfo from each client.
<script>
var socket = io.connect('http://localhost', {port: 8080});
socket.on('connect', function (data) {
socket.emit('storeClientInfo', { customId:"000CustomIdHere0000" });
});
</script>
Hope this helps.
To set custom socket id, generateId function must be overwritten. Both of eio and engine props of Socket.io server object can be used for to manage this operation.
A simple example:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.engine.generateId = function (req) {
// generate a new custom id here
return 1
}
io.on('connection', function (socket) {
console.log(socket.id); // writes 1 on the console
})
It seems to be it has been handled.
It must be in mind that socket id must be unpredictable and unique value with considering security and the app operations!
Extra: If socket.id is returned as undefined because of your intense processes on your generateId method, async/await combination can be used to overcome this issue on node.js version 7.6.0 and later. handshake method of node_modules/engine.io/lib/server.js file should be changed as following:
current:
// engine.io/lib/server.js
Server.prototype.generateId = function (req) {
return base64id.generateId();
};
Server.prototype.handshake = function (transportName, req) {
var id = this.generateId(req);
...
}
new:
// function assignment
io.engine.generateId = function (req) {
return new Promise(function (resolve, reject) {
let id;
// some intense id generation processes
// ...
resolve(id);
});
};
// engine.io/lib/server.js
Server.prototype.handshake = async function (transportName, req) {
var id = await this.generateId(req);
...
}
Note: At Engine.io v4.0, generateId method would accept a callback. So it would not needed to change handshake method. Only generateId method replacement is going to be enough. For instance:
io.engine.generateId = function (req, callback) {
// some intense id generation processes
// ...
callback(id);
};
In the newest socket.io (version 1.x) you can do something like this
socket = io.connect('http://localhost');
socket.on('connect', function() {
console.log(socket.io.engine.id); // old ID
socket.io.engine.id = 'new ID';
console.log(socket.io.engine.id); // new ID
});
I would use an object as a hash lookup - this will save you looping through an array
var clients = {};
clients[customId] = clientId;
var lookup = clients[customId];
Do not change the socket IDs to ones of your own choosing, it breaks the Socket.io room system entirely. It will fail silently and you'll have no clue why your clients aren't receiving the messages.
This will work with 2.2.0 and above version of Socket.IO
To set custom Socket Id, generateId function must be overwritten.
A simple example:
Server Side
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.use((socket, next) => {
io.engine.generateId = () => {
// USE ONE OF THESE
socket.handshake.query.CustomId; // this work for me
// return socket.handshake.query.CustomId;
}
next(null, true);
});
io.on('connection', function (socket) {
console.log(socket.id);
})
Clint Side
io.connect(URL, { query: "CustomId = CUSTOM ID IS HERE" })
NOTE: Keep in mind that socket id must be a unique value.
why not a simpler solution that does not need to maintain an array of connected clients and does not override internal socket id?
io.on("connection", (socket) => {
socket.on('storeClientInfo', (data) => {
console.log("connected custom id:", data.customId);
socket.customId = data.customId;
});
socket.on("disconnect", () => {
console.log("disconnected custom id:", socket.customId);
})
});
Client side
let customId = "your_custom_device_id";
socket.on("connect", () => {
socket.emit('storeClientInfo', { customId: customId });
});
or you can override the socket id, like this:
io.on('connection', function(socket){
socket.id = "YOUR_CUSTOM_ID";
});
you can see under the array:
io.sockets.sockets
Can store customId (example userId) in object format instead of for loop, this will improve performance during connection, disconnect and retrieving socketId for emitting
`
var userId_SocketId_KeyPair = {};
var socketId_UserId_KeyPair = {};
_io.on('connection', (socket) => {
console.log('Client connected');
//On socket disconnect
socket.on('disconnect', () => {
// Removing sockets
let socketId = socket.id;
let userId = socketId_UserId_KeyPair[socketId];
delete socketId_UserId_KeyPair[socketId];
if (userId != undefined) {
delete userId_SocketId_KeyPair[userId];
}
console.log("onDisconnect deleted socket with userId :" + "\nUserId,socketId :" + userId + "," + socketId);
});
//Store client info
socket.on('storeClientInfo', function (data) {
let jsonObject = JSON.parse(data);
let userId = jsonObject.userId;
let socketId = socket.id;
userId_SocketId_KeyPair[userId] = socketId;
socketId_UserId_KeyPair[socketId] = userId;
console.log("storeClientInfo called with :" + data + "\nUserId,socketId :" + userId + "," + socketId);
});
`
With this 2.2.0 version of Socket.IO, you can achieve this.
io.use((socket, next) => {
io.engine.generateId = () => socket.handshake.query.token;
next(null, true);
});
If you are trying to use a custom id to in order to communicate with a specific client then you can do this
io.on('connection', function(socket){
socket.id = "someId"
io.sockets.connected["someId"] = io.sockets.connected[socket.id];
// them emit to it by id like this
io.sockets.connected["someId"].emit("some message", "message content")
});

Categories

Resources