JavaScript - objects liefetime - javascript

I'm trying to create a node.js server using socket.io. At the moment is just proof on concept.
I created 2 files, first for server and anther for server side user.
server-core.js
'use strict';
var io = require('socket.io').listen(4567);
var user = require('./server-user');
var users = [];
io.sockets.on('connection', function(socket){
var su = new user.ServerUser(socket);
users[socket.id] = su;
socket.on('auth', su.auth);
socket.on('disconnect', su.disconnect);
});
io.sockets.on('disconnect', function(socket){
console.log('disconnect');
users[socket.id].disconnect();
});
console.log('Server started');
server-user.js
var ServerUser = (function(){
function ServerUser(socket){
this.socket = socket;
console.log('serverUser-ctor ' + this.socket)
}
ServerUser.prototype.auth = function(data){
console.log('auth received\r\n' + data);
this.socket.emit('auth', {
Id: data.Id,
Timestamp: data.Timestamp,
StringField: data.StringField
});
}
ServerUser.prototype.disconnect = function(){
console.log('Client disconnected');
}
return ServerUser;
})();
module.exports = {
ServerUser: ServerUser
};
my C# client connects fine to server, but when user-server tries to send the answer back the this.socket is undefined in ServerUser.prototype.auth method. This tell me that the instance of the ServerUser that I create in server-core is not being hold and when 'auth' method is called a new instance of object is actually being created.
To proof this I replaced this line
socket.on('auth', su.auth);
with such one
socket.on('auth', function(data){su.auth(data);});
After this it worked as needed.
Is this the correct way to write JS code?
Is there a better way to separate logic under separate files and classes when writing large node.js applications?
Thx for any opinions.

The problem is the invocation context. When you pass su.auth to socket.on(), this no longer refers to su inside of auth. So, there are a couple of ways to fix that. Using an anonymous function, as you found, is one. Function.bind is another:
socket.on('auth', su.auth.bind(su));

Related

JavaScript issue with import/export modules and Globally defined variables

(note, I'm using Node.js to create a telnet server and handle user input)
I'm having issues using variables/arrays in javascript inside functions in other scripts.
Let me set a simpler example:
var connections = []
is an array in my main.js
Broadcast.js is a function I put in a separate module and attempt to use the broadcast() function in my main.js.
The error I get is stating that connections is undefined. How do get Broadcast.js able to see the connections array in the main.js
~~
For example in my main.js I set an array to handle clients connecting to a server
//point to Telnet library
const Telnet = require('ranvier-telnet');
const logger = require('./logger.js')
var outspeak = []
var connections = []
var clients = []
let server = new Telnet.TelnetServer(rawSocket => {
let telnetSocket = new Telnet.TelnetSocket();
//attaching socket
telnetSocket.attach(rawSocket);
//setting telnet options
telnetSocket.telnetCommand(Telnet.Sequences.WILL, Telnet.Options.OPT_EOR);
//giving clients a name
telnetSocket.name = rawSocket.remoteAddress + ":" + rawSocket.remotePort
//pushing client names to array
clients.push(telnetSocket.name);
//pushing client connections to an array
connections.push(rawSocket);
console.log(`${telnetSocket.name} has connected`)
logger(`${telnetSocket.name} has connected`)
broadcast(telnetSocket.name + " connected.")
telnetSocket.on('data', function (data) {
//broadcast (telnetSocket.name + ">" + data, telnetSocket);
}
function broadcast (message, sender) {
connections.forEach(function (connection) {
//don't want to send it to sender
if (connection === sender) return;
connection.write(`${message} \n`);
});
}
Now inside my main script, I could called that array/push to that array, read from that array, as long as I type out the function inside the main.js file.
And it can easily use the broadcast function.
Now I want to make it more advance and make reduce my lines on my main.js
but once I separate the broadcast function into it's own module.
use strict'
//broadcast function
function broadcast (message, sender) {
connections.forEach(function (connection) {
//don't want to send it to sender
if (connection === sender) return;
connection.write(`${message} \n`);
});
}
module.exports = broadcast
I get a connection undefined error any time I try to invoke that broadcast function. It's like my global variable/array can't be seen by broadcast.js function.
this is how I'm invoking it
// handle input
telnetSocket.on('data', function (data) {
broadcast (telnetSocket.name + ">" + data, telnetSocket);
});
And yes, const broadcast = require('./broadcast.js'); as been added to the file at the top.
Here's the broken code complete:
'use strict'
//point to Telnet library
const Telnet = require('ranvier-telnet');
const logger = require('./logger.js');
const broadcast = require('./broadcast.js');
var connections = []
var clients = []
//had to call message as global variable
//Asan's timestamp functionm
//telnetstuff
console.log("Starting...");
let server = new Telnet.TelnetServer(rawSocket => {
let telnetSocket = new Telnet.TelnetSocket();
//attaching socket
telnetSocket.attach(rawSocket);
//setting telnet options
telnetSocket.telnetCommand(Telnet.Sequences.WILL, Telnet.Options.OPT_EOR);
//giving clients a name
telnetSocket.name = rawSocket.remoteAddress + ":" + rawSocket.remotePort
//pushing client names to array
clients.push(telnetSocket.name);
//pushing client connections to an array
connections.push(rawSocket);
console.log(`${telnetSocket.name} has connected`)
logger(`${telnetSocket.name} has connected`)
broadcast(telnetSocket.name + " connected.")
// handle input
telnetSocket.on('data', function (data) {
broadcast (telnetSocket.name + ">" + data, telnetSocket);
});
//removing client/connection from array
rawSocket.on('end', function () {
clients.splice(clients.indexOf(telnetSocket), 1);
connections.splice(connections.indexOf(rawSocket), 1);
broadcast(telnetSocket.name + " has left.\n");
logger(telnetSocket.name + " has left.");
console.log(telnetSocket.name + " has left.");
});
}).netServer
server.listen(4000);
console.log('ServerRunning...');
logger('>Server started.');
What I'm missing here? Also I apologize in advance this is my first question ever asked and I've gone through as much I could today to even figure out how to ask my question, maybe I'm not using correct lingo/terms? any help is appreciative.
refactor\broadcast.js:5
connections.forEach(function (connection) {
^
ReferenceError: connections is not defined
In nodejs, when you declare a variable not inside any function definitions, it is scoped to the file only. (This is different from browser javascript.) If you want something to be accessible from outside, you need to export it:
module.exports.connections = connections;
Then import it into the other file:
const connections = require(myFile);
This will work as long as you don't try to set the value of the variable in either file, but if you do that they'll end up pointing to separate objects. But mutating it, calling methods on it, etc should work fine.

socket.io get socket inside a callback

I'm using socket.io for node.js. If you add an event handler like this:
io = require('socket.io')(http);
io.on('connect',function(socket){
socket.on('some event',function(arg1,arg2){
/// using socket to emit events for example
}
}
Then I can access socket inside the callback for the 'some event'
However what if I use it like this
io = require('socket.io')(http);
io.on('connect',function){
socket.on('some event',myfunction);
}
function myFunction(arg1,arg2)
{
//I want to use calling socket here.
}
How do I access socket in the later case?
I need the socket to get the socket.id so I can know who called this event.
Thanks
Well, if I understand what you want to do, you can simply:
io = require('socket.io')(http);
io.on('connect',function){
socket.on('some event',myfunction);
}
function myFunction(arg1,arg2)
{
var socketId = this.id; //here you go, you can refer the socket with "this"
}
This is what I usually do to keep the code clean:
var on_potato = function(potatoData){
var socket = this;
var id = socket.id;
//use potatoData and socket here
};
var on_tomato = function(tomatoData){
var socket = this;
var id = socket.id;
//use tomatoData and socket here
};
var handleClientConnection = function (client) {
client.on('potato', on_potato);
client.on('tomato', on_tomato);
};
io.on('connection', handleClientConnection)
Alright, so after discussing this a potential solution is to just invoke your named function from within the anoymous callback function passed to the on method.
io.on('connect', function(socket){
socket.on('someEvent', function(username, date){
// If you emitted an object, you'll need to parse the incoming data. So say
// You emitted {username: 'SomeName', date: 'SomeDate' }
// You could just pass data.username and data.date directly
// or put them into local variables, like:
//var username = data.username, date = data.date;
// Invoke your named function here and you can pass
// whatever you want to it, along with the socket
myFunction(username, date, socket)
})
})
myFunction(username, date, socket){
// Do whatever you're doing with the passed paramaters
}
I've used Lodash's partial function quite often to solve problems like this (Underscore has one as well, which does the same thing). Basically what it does is create a new function that has some of the original function's arguments filled in. So what you would do is something like this:
io = require('socket.io')(http);
io.on('connect', function(socket) {
socket.on('some event', _.partial(myfunction, socket));
});
function myFunction(socket, ...args) {
// whatever you wanna do
}
Then, when the new curried function is returned from the partial execution, it has socket prefilled as the first param and you can use it how you'd like.
Just a note that the ...args is just a placeholder for whatever you want to put there. Also, I'm not sure if socket.io passes anything into the function on firing the callback, which may affect the placement of the arguments into the curried function. If socket, shouldn't be the first argument, you can make it the second thusly:
io = require('socket.io')(http);
io.on('connect', function(socket)){
socket.on('some event', _.partial(myfunction, _, socket));
}
function myFunction(arg1, socket, ...args) {
// whatever you wanna do
}

How to send data to specific client in a specific room with socket io

My question is what's the proper way of sending data to a specific client in a specific room. I'm using socket io and the code written below:
I use the command:
socket.to(socket.id).emit('change', {data})
but the client never gets this command. Anyone know why?
Below is a snippet of my code:
server code:
io.on('connection', function (socket) {
socket.on('channelJoin', function(channel){
socket.join(channel);
if(deltasByChannel[channel])
{
console.log("sending initial data to: "+socket.id);
socket.to(socket.id).emmit('change', deltasByChannel[channel]);
}
socket.on("change", function(delta){
console.log("channel: " + channel+" was edited!");
console.log(deltasByChannel[channel]);
deltasByChannel[channel] ? deltasByChannel[channel] = deltasByChannel[channel].concat(delta) : deltasByChannel[channel] = delta;
socket.broadcast.to(channel).emit('change', delta);
});
});
});
http.listen(3000, function () {
console.log('listening on *:3000');
});
client code:
var channel = window.location.pathname;
var socket = io.connect();
//Ace handlers
var sendUpdateData = function(e){
socket.emit("change", [e.data]);
};
socket.on('connect', function(){
socket.on("change", function(data){
console.log("change event received!");
editor.getSession().removeListener('change', sendUpdateData);
editor.getSession().getDocument().applyDeltas(data);
editor.getSession().on('change', sendUpdateData);
});
editor.getSession().on('change', sendUpdateData);
socket.emit('channelJoin', channel);
});
just to avoid confusion the editor object is listening for a change event as well. It's from an entirely different library (ace.js) that has nothing to do with my socket io issue.
below is another snippet of the server code for more clarity:
var http = require('http').Server(app);
var io = require('socket.io')(http);
http.listen(3000, function () {
console.log('listening on *:3000');
});
I think there's some confusion about sending data over sockets using socket.io. You can elect to emit events or data using rooms or private namesspaces, you can broadcast to all connected sockets, or you can emit data to a specific ID.
In your case you should just be selecting a socket.id, to emit an event to a particular connection. You can do this by:
io.sockets.connected[ socket.id ].emit('privateMsg', 'hello this is a private msg');
You can also use the to() method in conjunction with broadcast as well:
socket.broadcast.to( socket.id ).emit('privateMsg', 'hello this is a private msg');
This will reach the user which matches the socket.id you pass in as the argument.
To contact users within a "room" or private namespace, you can also use the to() method:
io.to('some room').emit('some event');
In this case some room would be the channel var you've defined, and it should match a predefined variable that has already been instantiated.
For more information about rooms/namespaces/and reaching specific socket connections: http://socket.io/docs/rooms-and-namespaces/#

Best approach for a modular node.js/socket.io/express application

I'm currectly creating an app using Node.JS that makes use of Express and Socket.io. As time progresses it's becoming increasingly difficult to deal with one file, I'm in the process of moving certain things out that I know how but was wondering on the best approach to do this.
I have a private area constructor similar to:
privateArea.js
function privateArea(props) {
this.id = props.id;
this.name = props.name;
this.users = [];
}
privateArea.prototype.addUser = function(socketId) {
this.users.push(socketId);
};
module.exports = privateArea;
I'd like to have this also have access to the socket.io variable that's been setup for use in a separate sockets.js file that can be included via the main app.js and a seperate file for express.js
So I'd like the structure as follows:
project
| app.js - joins it all together
| express.js - initialises and manages all express routing
| privateArea.js - constructor for private areas - must be able to reference socket.io
| sockets.js - initialises and manages all socket.io sockets and events
Any help/examples would be very appreciated.
Thanks
I use socket.io and express quite often in my projects, and I've developed a template which makes things easy. I like to have a fail-over in case the socket connections drops for some reason, or if a socket connection cannot be established. So I create http channels as well as socket channels. Here's a basic module template:
module.exports = function () {
var exported = {};
var someFunction = function (done) {
//.. code here..//
if (typeof done === "function") {
done(null, true);
}
};
// export the function
exported.someFunction = someFunction;
var apicalls = function (app) {
app.get("/module/someFunction", function (req, res) {
res.header("Content-Type", "application/json");
someFunction(function (err, response) {
if (err) return res.send(JSON.stringify(err));
res.send(JSON.stringify(response));
});
});
};
exported.apicalls = apicalls;
var socketcalls = function (io) {
io.on("connection", function (socket) {
socket.on('module-someFunction', function () {
someFunction(function (err, response) {
if (err) return socket.emit('module-someFunction', err);
socket.emit('module-someFunction', response);
});
});
});
};
exported.socketcalls = socketcalls;
return exported;
}
So to use this, I'd first need to include the module in my app.js file like this:
var mymod = require('./myModule.js');
And then I can enable access to this service from HTTP and over the websocket like this:
mymod.apicalls(app); // passing express to the module
mymod.socketcalls(io); // passing socket.io to the module
Finally, from the front-end, I can check to see if I have a socket connection, and if so, I use the socket to emit "module-someFunction". If I don't have a socket connection, the front-end will do an AJAX call instead to "/module/someFunction" which will hit the same function on the server side that it would've had I used the socket connection.
As an added bonus, if I need to utilize the function within the server, I could do that as well since the function is exported. That would look like this:
mymod.someFunction(function (err, response) {
// ... handle result here ... //
});

Node.js module level variables are undefined

I'm having an issue accessing the scope of a module level variable from within a function inside of said module. See below...
var socketio = require('socket.io');
var socket = socketio.listen();
var myCustomModule = require('./lib/mycustommodule')('http://mysite:8080');
socket.on('connection', function connection(socket) {
socket.emit('message', {data:"test1"}); <====THIS WORKS
socket.on('init', function init(data) {
socket.emit('message', {data:"test1"}); <====THIS WORKS
refreshMyCustomModule();
});
});
var refreshMyCustomModule = function() {
socket.emit('message', {data:"test1"}); <=====THIS DOESN'T WORK
myCustomModule.beginSomeAsyncTask(function(data) { <======THIS DOESN'T WORK
socket.emit('message', {data:"test2"}); <========THIS DOESN'T WORK
});
};
Looking at the sample above. When I call my refreshMyCustomModule function suddenly socket and myCustomModule become undefined. I've also tried using this as well as setting up a var self = this.
I've written a bunch in javascript on the client but when coding in node.js it seems like scoping is different and I just can't crack this nut.
Note that the socket at the global level of your script and socket within your function connection are two different variables. The one inside your function connection is the argument that was passed into that function from the connection event. The one you're using in refreshMyCustomModule is the global one, the one on which you called listen.
This is clearer if we change their names, since they're different variables:
var socketio = require('socket.io');
var socketUsedForListen = socketio.listen();
var myCustomModule = require('./lib/mycustommodule')('http://mysite:8080');
socketUsedForListen.on('connection', function connection(socketFromConnection) {
socketFromConnection.emit('message', {data:"test1"});
socketFromConnection.on('init', function init(data) {
socketFromConnection.emit('message', {data:"test1"});
refreshMyCustomModule();
});
});
var refreshMyCustomModule = function() {
socketUsedForListen.emit('message', {data:"test1"});
myCustomModule.beginSomeAsyncTask(function(data) {
socketUsedForListen.emit('message', {data:"test2"});
});
};
I'm reasonably certain you meant to use socketFromConnection in refreshMyCustomModule, not socketUsedForListen. If so, either move the refreshMyCustomModule into your connection callback, or pass the socket into it as an argument.
It's because you're using an async event in socket.on('init') that leads to the scope issue. I believe if you pass in the parameters you want to use from the parent it will work. e.g.:
var socketio = require('socket.io');
var socket = socketio.listen();
var myCustomModule = require('./lib/mycustommodule')('http://mysite:8080');
socket.on('connection', function connection(socket) {
socket.emit('message', {data:"test1"}); <====THIS WORKS
socket.on('init', function init(data) {
socket.emit('message', {data:"test1"}); <====THIS WORKS
refreshMyCustomModule(socket, myCustomModule);
});
});
var refreshMyCustomModule = function(socket, module) {
socket.emit('message', {data:"test1"}); <=====THIS DOESN'T WORK
module.beginSomeAsyncTask(function(data) { <======THIS DOESN'T WORK
socket.emit('message', {data:"test2"}); <========THIS DOESN'T WORK
});
};

Categories