Ways to test for an available port

It's a Unix System! I know this!

In case you don't know, you can test whether or not a port is in use on your system with namp.

nmap -p 36367 localhost

There's a less elegant way to do it with netstat.

netstat --all --numeric --tcp | grep ':22'

You could also test the failure case with netcat, but you can't easily test the success case because it would start listening on that port.

sudo netcat -l 22
# netcat: Address already in use
netcat -l 22
# (just listens and waits, hit ctrl+c to stop)

A programmatic solution

For this solution I didn't want to install 50MiB of nmap. I wanted something I could test programatically. I decided to do it in go.

Doing it in Go

Here's the heart of it:

  port := "36367"

  ln, err := net.Listen("tcp", ":" + port)

  if err != nil {
    fmt.Fprintf(os.Stderr, "Can't listen on port %q: %s", port, err)
    os.Exit(1)
  }

  _ := ln.Close()
  fmt.Printf("TCP Port %q is available", port)
  os.Exit(0)

And the full thing in context:

package main

import "net"
import "fmt"
import "flag"
import "os"
import "strconv"

func usage() {
    fmt.Fprintf(os.Stderr, "usage: test-local-port [port number]\n")
    flag.PrintDefaults()
    os.Exit(2)
}

func main() {
  flag.Usage = usage
  flag.Parse()

  args := flag.Args()
  if len(args) < 1 {
      fmt.Fprintf(os.Stderr, "Input port is missing.")
      os.Exit(1)
  }

  port := args[0]
  _, err := strconv.ParseUint(port, 10, 16)
  if err != nil {
    fmt.Fprintf(os.Stderr, "Invalid port %q: %s", port, err)
    os.Exit(1)
  }

  ln, err := net.Listen("tcp", ":" + port)

  if err != nil {
    fmt.Fprintf(os.Stderr, "Can't listen on port %q: %s", port, err)
    os.Exit(1)
  }

  err = ln.Close()
  if err != nil {
    fmt.Fprintf(os.Stderr, "Couldn't stop listening on port %q: %s", port, err)
    os.Exit(1)
  }

  fmt.Printf("TCP Port %q is available", port)
  os.Exit(0)
}

Doing it in Node

For reference, here's the same task written with node / io.js.

'use strict';

var net = require('net');
var server = net.createServer();
var port = process.argv[2];

if (!(port && port < 65536)) {
  console.error("Invalid port '%s'", port);
  process.exit(1);
}

server.on('error', function (e) {
  console.error("TCP port '%s' is taken: '%s'", port, e);
  process.exit(1);
});

// Note old versions of node must use the old syntax
// server.listen(port, function () { ... });
server.listen({ port: port, exclusive: true }, function () {
  server.close(function () {
    console.log("TCP port '%s' is available", port);
    process.exit(0);
  });
});

Note that if you're using an old version of node (most versions of node) rather than io.js, you may have to resort to the old listen syntax.

Tradeoffs

One of the targets for what I'm doing with this is Raspberry Pi. The VM initialization time of node.js is significant (seconds). The filesize for the go version is significant (megabytes).

This is also something that I could have reasonably done in C, but hey, who wants to do that? Not me.


By AJ ONeal

If you loved this and want more like it, sign up!


Did I make your day?
Buy me a coffeeBuy me a coffee  

(you can learn about the bigger picture I'm working towards on my patreon page )