How to test if a port is available (in Go)
Published 2015-6-20Ways 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
Did I make your day?
Buy me a coffee
(you can learn about the bigger picture I'm working towards on my patreon page )