In my node.js application I have a collection of client sockets as an array. When a communication error occurs, I simply call destroy on the socket.
My question is: should I destroy the socket before or after removing it from the array? The documentation doesn't say much.
var clientSockets = []
var destroySocketBefore = function(socket) {
socket.destroy()
var socketIdx = clientSockets.indexOf(socket)
if (socketIdx > -1) {
clientSockets.splice(socketIdx, 1)
}
}
var destroySocketAfter = function(socket) {
var socketIdx = clientSockets.indexOf(socket)
if (socketIdx > -1) {
clientSockets.splice(socketIdx, 1)
}
socket.destroy()
}
In the case of destroySocketBefore, I am not sure if the socket will be found in the array if I destroy it before searching for it, so there is a possibility that array still incorporates invalid sockets in subsequent logic.
In the case of destroySocketAfter, I am not sure if calling destroy on a socket that was removed from array will have the desired result. Is there a possibility that the system will delete the socket object after splicing the array, so sometimes I get to call destroyon a null object.
I tested and it seems that both methods work as there is no difference between them, so I am not sure which method is the correct one.
Either solution is valid and the two are effectively the same. The destroyed socket will get removed no matter what since there are no race conditions or anything like that (since javascript execution in node all happens on the same thread).
splice will only remove the socket from an user defined array and will have no effect to it been closed, therefore the second method is the best option according to your answers.
Related
So, I want to move a websocket instance on my Node.js server from one array to the other. I use JSON.parse(JSON.stringify(theitem)), and it sticks in there just fine - but then it gives me a peculiar error.
var ws = require("ws");
var server = new ws.Server({server:app.server, path:"/whatever", port:8080})
var clients = [];
var OtherClients = [];
server.on("connection", function(socket){
socket.send("Hello!"); // This works.
socket.on("message", function(msg){console.log("whatever");})
clients.push({socket:socket, OtherInformation:"whatever!"});
NextFunction();
})
function NextFunction(){
for(let i = 0; i < clients.length; i+=2){ // Every second one, meant to represent arbitrary logic...
clients.forEach(TheClient=>{
TheClient.socket.send("Arbitrary string!"); // This one works.
})
}
OtherClients[0] = JSON.parse(JSON.stringify(clients[clients.length-1]));
clients.splice(0,1);
OtherClients[0].socket.send("Another arbitrary string!"); // Crash here.
}
This gives me the error TypeError: OtherClients[0].socket.send is not a function. How?
Moreover, I noticed that socket.send is not actually a member of the socket object (even in the original version where I can send it). What's going on?
Am I really not allowed to copy a WebSocket object, or am I missing the point entirely?
You can't stringify a webSocket object and then parse it back into a functioning object. A webSocket object represents a local TCP connection and can't easily be serialized like that.
If you just want to move the socket object from one array to another, you can just take the socket object and add it to another array, no serialization to JSON necessary. You could do it like this:
// remove last socket from clients array and
// add it to the beginning of the OtherClients array
OtherClients.unshift(clients.pop());
I am very new to node js and socket io. Can this code lead to a race condition on counter variable. Should I use a locking library for safely updating the counter variable.
"use strict";
module.exports = function (opts) {
var module = {};
var io = opts.io;
var counter = 0;
io.on('connection', function (socket) {
socket.on("inc", function (msg) {
counter += 1;
});
socket.on("dec" , function (msg) {
counter -= 1;
});
});
return module;
};
No, there is no race condition here. Javascript in node.js is single threaded and event driven so only one socket.io event handler is ever executing at a time. This is one of the nice programming simplifications that come from the single threaded model. It runs a given thread of execution to completion and then and only then does it grab the next event from the event queue and run it.
Hopefully you do realize that the same counter variable is accessed by all socket.io connections. While this isn't a race condition, it means that there's only one counter that all socket.io connections are capable of modifying.
If you wanted a per-connection counter (separeate counter for each connection), then you could define the counter variable inside the io.on('connection', ....) handler.
The race conditions you do have to watch out for in node.js are when you make an async call and then continue the rest of your coding logic in the async callback. While the async operation is underway, other node.js code can run and can change publicly accessible variables you may be using. That is not the case in your counter example, but it does occur with lots of other types of node.js programming.
For example, this could be an issue:
var flag = false;
function doSomething() {
// set flag indicating we are in a fs.readFile() operation
flag = true;
fs.readFile("somefile.txt", function(err, data) {
// do something with data
// clear flag
flag = false;
});
}
In this case, immediately after we call fs.readFile(), we are returning control back to the node.js. It is free at that time to run other operations. If another operation could also run this code, then it will tromp on the value of flag and we'd have a concurrency issue.
So, you have to be aware that anytime you make an async operation and then the rest of your logic continues in the callback for the async operation that other code can run and any shared variables can be accessed at that time. You either need to make a local copy of shared data or you need to provide appropriate protections for shared data.
In this particular case, the flag could be incremented and decremented rather than simply set to true or false and it would probably serve the desired purpose of keeping track of whether this file is current being read or not.
Shorter answer:
"Race condition" is when you execute a series of ordered asynchronous functions and because of their async nature they won't finish processing in their original order.
In your code, you are executing a series of ordered synchronous process (increasing or decreasing the counter), So they finish instantly after they start, resulting in ordered output. So no racing here!
I would like to get the data from a client (AR.Drone 2.0) just once and them store it in a variable to be printed. I have used:
client.on('navdata', console.log);
however, when I execute this command data is printed more than once and I have to stop the script to stop this process. How can I get the data just once and store it in a variable.
Client object inherits EventEmitter, so you should be able to use once() to listen for navdata event only once. To store the emitted value to a variable you can do something like:
var _navData;
// ...
client.once('navdata', function (navData) {
_navData = navData;
});
Update
Regarding to your comment, I suggest you to declare a function that gets a navdata object as an argument and pass that function to client.once():
var doSomethingWithNavData = function doSomethingWithNavData(navData) {
console.log(navData);
// do what ever you want to do with navData...
}
client.once('navdata', doSomethingWithNavData);
There are two EventEmitter methods for adding listeners: .on() and .once().
.on() actively listens and catches events until .removeListener() or .removeAllListeners() are called to remove the listener.
.once() listens for the next event and removes itself automatically (effectively running once), unless .removeListener() or .removeAllListeners() are called to remove it before it does.
It seems that socket.io cannot send the list of connected users, like this:
socket.emit('users', sIo.sockets.clients());
It gives me the following error:
/Users/enrico/Desktop/helloExpress/node_modules/socket.io/lib/parser.js:75
data = JSON.stringify(ev);
^
TypeError: Converting circular structure to JSON
Apparently it cannot stringify the returned value from sIo.sockets.clients() Any ideas on how to fix this? Thanks in advance.
Since the problem is a circular reference there no real 'fixing it'. Circular reference means that some object in the structure points to another part of the object, making an infinite loop. What you can do is something like this.
var returnList = [];
var clients = sIo.sockets.clients(); // grab list of clients, assuming its an array
for(var i = 0; i < clients.length; i++) {
var client = clients[i]; // next client in array
// push values into return list
returnList.push({
name: client.name,
someOther: client.value,
another: client.thing
});
}
// emit cleaned up list
socket.emit('users', returnList);
With this code you can cherry pick the values you want and send only those. This is good for several other reasons. Since this clients list is likely an internal implementation is might also send information about other clients connection.
This is all also pretty speculative as I'm not 100% what libraries you're using, looks like Socket.IO but I cannot find any socket.clients() method.
I'm using Redis in my application and one thing is not clear for me. I save an object with a random generated string as its key. However I would like to check if that key exists. I am planning to use while loop however I am not sure how would I struct it according to Redis. Since if I would like to check for once, I would do;
redisClient.get("xPQ", function(err,result){
if(result==null)
exists = false
});
But I would like use the while loop as;
while(exists == false)
However I cannot build the code structure in my head. Would the while be inside the function or outside the function?
In general, you shouldn't check for existence of a key on the client side. It leads to race conditions. For example, another thread could insert the key after the first thread checked for its presence.
You should use the commands ending with NX. For example - SETNX and HSETNX. These will insert the key only if doesn't already exist. It is guaranteed to be atomic.
I do not understand why you need to implement active polling to check whether a key exists (there are much better ways to handle this kind of situations), but I will try to answer the question.
You should not use a while loop at all (inside or outside the function). Because of the asynchronous nature of node.js, these loops are better implemented using tail recursion. Here is an example:
var redis = require('redis')
var rc = redis.createClient(6379, 'localhost');
function wait_for_key( key, callback ) {
rc.get( key, function(err,result) {
if ( result == null ) {
console.log( "waiting ..." )
setTimeout( function() {
wait_for_key(key,callback);
}, 100 );
} else {
callback(key,result);
}
});
}
wait_for_key( "xPQ", function(key,value) {
console.log( key+" exists and its value is: "+value )
});
There are multiple ways to simplify these expressions using dedicated libraries (using continuation passing style, or fibers). For instance you may want to check the whilst and until functions of the async.js package.
https://github.com/caolan/async