Find free port not in use for apps - find some algorithm

I use the following API in my program to detrmine free port and provide it to application to run

portscanner.findAPortNotInUse(3000, 65000, '127.0.0.1', function(error, port) {
  console.log('AVAILABLE PORT AT: ' + port)
})

https://github.com/baalexander/node-portscanner

This free port are given to application for use and working OK. The problem is that if I provide a free port to application A and the application is doesn't occupied it yet(sometimes it takes some time...) and there is coming other application B and request a free port so it give to APP B the port of app A Which cause to problem... is there any elegant way to solve it?

my application doesn't have state so it cannot save to which app get which port...

There is solution that we can randomize the range but this is not robust ...

In my application Im getting the URL of the app that I should provide the free port to run.

update

I cannot use some broker or someting else that will controll this outside I need to find some algorithm (maybe with some smart random ) that can help me to do it internally i.e. my program is like singleton and I need some trick how to give port between 50000 to 65000 that will reduce the amount of collision of port that was provided to the apps

update 2

I've decided to try something like the following what do you think ?

using lodash https://lodash.com/docs/4.17.2#random to determine ports between with loops that provide 3(or more if that make sense) numbers for ranges like following

portscanner.findAPortNotInUse([50001, 60000, 600010], '127.0.0.1', function(err, port) {
    if(err) {
        console.log("error!!!-> " +err);
    }else {
        console.log('Port Not in Use ' + port);
    }

//using that in a loop 
var aa = _.random(50000, 65000); 

Then If I got false in the port i.e. all 3 port are occupied ,run this process again for 3 other random number.comments suggestion are welcomed!!! I try to find some way to avoid collision as much as possible...

Answers:

Answer

I would simply accept the fact that things can go wrong in a distributed system and retry the operation (i.e., getting a free port) if it failed for whatever reason on the first attempt.

Luckily, there are lots of npm modules out there that do that already for you, e.g. retry.

Using this module you can retry an asynchronous operation until it succeeds, and configure waiting strategies, and how many times it should be retried maximally, and so on…

To provide a code example, it basically comes down to something such as:

const operation = retry.operation();

operation.attempt(currentAttempt => {
  findAnUnusedPortAndUseIt(err => {
    if (operation.retry(err)) {
      return;
    }

    callback(err ? operation.mainError() : null);
  });
});

The benefits of this solution are:

  • Works without locking, i.e. it is efficient and makes low usage of resources if everything is fine.
  • Works without a central broker or something like that.
  • Works for distributed systems of any size.
  • Uses a pattern that you can re-use in distributed systems for all kinds of problems.
  • Uses a battle-tested and solid npm module instead of handwriting all these things.
  • Does not require you to change your code in a major way, instead it is just adding a few lines.

Hope this helps :-)

Answer

If your applications can open ports with option like SO_REUSEADDR, but operation system keeps ports in the list in TIME_WAIT state, you can bind/open port you want to return with SO_REUSEADDR, instantly close it and give it back to application. So for TIME_WAIT period (depending on operation system it can be 30 seconds, and actual time should be decided/set up or found by experiment/administration) port list will show this port as occupied.

If your port finder does not give port numbers for ports in TIME_WAIT state, problem solved by relatively expensive open/close socket operation.

Answer

I'd advise you look for a way to retain state. Even temporary state, in memory, is better than nothing at all. This way you could at least avoid giving out ports you've already given out. Because those are very likely not free anymore. (This would be as simple as saving them and regenerating a random port if you notice you found a random port you've already given out). If you don't want collisions, build your module to have state so it can avoid them. If you don't want to do that, you'll have to accept there are going to be collisions sometimes when there don't need to be.

If the URLs you get are random, the best you can do is guess randomly. If you can derive some property in which the URLs uniquely and consistently differ, you could design something around that.

Code example:

function getUnusedPort(url) {
  // range is [0, 65001). (inclusive zero, exclusive 65001)
  const guessPort = () => Math.floor(Math.random() * 15001) + 50000;
  let randomPort = guessPort();
  while (checkPortInUse(randomPort)) {
    randomPort = guessPort();
  }
  return randomPort;
}

Notes:

  • checkPortInUse will probably be asynchronous so you'll have to accommodate for that.
  • You said 'between 50000 and 65000'. This is from 50000 up to and including 65000.
Answer

When managing multiple applications or multiple servers, where one must be right the first time (without retrying), you need a single source of truth. Applications on the same machine can talk to a database, a broker server or even a file, so long as the resource is "lockable". (Servers work in similar ways, though not with local files).

So your flow would be something like:

  1. App A sends request to service to request lock.
  2. When lock is confirmed, start port scanner
  3. When port is used, release lock.

Again, this could be a "PortService" you write that hands out unused ports, or a simple lock in some shared resource so two things are getting the same port at the same time.

Hopefully you can find something suitable to work for your apps.

Answer

have you tried something like this ?

var portscanner = require('portscanner')

module.exports = function (ip, minport) {

  var pendings = [];
  var allocated = [];
  var pendingsearch = false;

  function start(){
    if (!pendingsearch && pendings.length) {
      pendingsearch = true;
      var ready = pendings.shift();
      var min = getMax();
      min = min===-1?minport:min+1;
      var max = min + 100;
      portscanner.findAPortNotInUse(min, max, ip, function(err, port) {
        if (err) {
          pendings.unshift(ready);
        } else if (allocated.indexOf(port)>-1) {
          pendings.unshift(ready);
        } else {
          allocated.push(port);
          ready(port);
        }
        restart();
      })
    }
  }
  function restart(){
    pendingsearch = false;
    setTimeout(start, 0);
  }
  function getMax(){
    var max = -1;
    allocated.forEach(function(p) {
      if (p>max) {
        max = p
      }
    })
    return max
  }

  function findPort(ready) {
    pendings.push(ready);
    start();
  };

  function updateAllocated() {
    var tocheck = [].concat(allocated);
    var todo = tocheck.length;
    tocheck.forEach(function(port, index) {
      portscanner.checkPortStatus(port, ip, function(error, status) {
        if (status==='closed') {
          allocated.splice(allocated.indexOf(port), 1);
        }
        if (index===tocheck.length-1) {
          setTimeout(updateAllocated, 1000);
        }
      })
    });
    if (tocheck.length<1) {
      setTimeout(updateAllocated, 1000);
    }
  };
  var update = setTimeout(updateAllocated, 1000);

  return {
    findPort: findPort,
    stop: function(){
      clearTimeout(update);
    },
  }
};
Answer

As you want to find an port that is not in use in your application, you could do is run following command:

netstat -tupln | awk '{print $4}' | cut -d ':' -f2

so in your application you will use this like:

const exec = require('child_process').exec;
exec('netstat -tupln | awk '{print $4}' | cut -d ':' -f2', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  var listPorts = stdout.split(/\n/)
  console.log(listPorts); // list of all ports already in use
  var aa = _.random(50000, 65000); // generate random port
  var isFree = (listPorts.indexOf(aa)===-1) ? true : false;
  if(isFree){
          //start your appliation         
  }else{
         // restart the search, write this in a function and start search again 
  } 

});

this should give you list of all ports that are in use,so use any port except ones in the listPorts.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.