Building a Networking CLI Tool in Go (A Step-By-Step Tutorial)

Building a Networking CLI Tool in Go (A Step-By-Step Tutorial)
Photo by Shubham Dhage / Unsplash

Go is one of the best languages for building CLI tools — it compiles to tiny static binaries, has a rock-solid standard library, and makes concurrency effortless.
In this tutorial, we’ll build a network scanner CLI that:

  • Accepts a list of hosts
  • Sends a ping-like TCP check to each
  • Measures latency
  • Runs the checks concurrently
  • Prints results in a clean, CLI-friendly table

This kind of tool is great for debugging networking issues, monitoring small clusters, or just learning Go’s networking primitives.


🚀 What We’re Building: netscan

Our CLI will:

  1. Accept hosts via command-line flags
  2. Attempt a fast TCP connection on port 80 (configurable)
  3. Time the connection
  4. Report whether each host is reachable
  5. Do it all concurrently using goroutines

Example usage:

netscan --hosts google.com,github.com,example.com --port 80

Output:

HOST             STATUS      LATENCY
google.com       OK          32ms
github.com       OK          45ms
example.com      FAILED      timeout

📁 Project Structure

netscan/
├── main.go
└── go.mod

🛠 Step 1: Initialise the Project

mkdir netscan
cd netscan
go mod init netscan

🌐 Step 2: Write the CLI App

Create main.go:

package main

import (
	"flag"
	"fmt"
	"net"
	"strings"
	"time"
)

type ScanResult struct {
	Host    string
	Success bool
	Latency time.Duration
	Err     error
}

func scanHost(host string, port int, timeout time.Duration) ScanResult {
	addr := fmt.Sprintf("%s:%d", host, port)

	start := time.Now()
	conn, err := net.DialTimeout("tcp", addr, timeout)
	latency := time.Since(start)

	if err != nil {
		return ScanResult{Host: host, Success: false, Latency: latency, Err: err}
	}
	conn.Close()

	return ScanResult{Host: host, Success: true, Latency: latency, Err: nil}
}

func main() {
	hostsFlag := flag.String("hosts", "", "Comma-separated list of hosts")
	portFlag := flag.Int("port", 80, "Port to scan")
	timeoutFlag := flag.Duration("timeout", 2*time.Second, "Connection timeout (e.g. 500ms, 2s)")
	flag.Parse()

	if *hostsFlag == "" {
		fmt.Println("Please provide hosts: --hosts host1,host2,host3")
		return
	}

	hosts := strings.Split(*hostsFlag, ",")

	results := make(chan ScanResult)

	for _, host := range hosts {
		h := strings.TrimSpace(host)
		go func() {
			results <- scanHost(h, *portFlag, *timeoutFlag)
		}()
	}

	fmt.Printf("%-15s %-10s %-10s\n", "HOST", "STATUS", "LATENCY")

	for range hosts {
		r := <-results

		status := "OK"
		if !r.Success {
			status = "FAILED"
		}

		lat := r.Latency.String()
		if !r.Success {
			lat = "timeout"
		}

		fmt.Printf(
			"%-15s %-10s %-10s\n",
			r.Host, status, lat,
		)
	}
}

▶️ Step 3: Run Your CLI

go run . --hosts google.com,github.com,example.com --port 80

Try scanning an unreachable host:

go run . --hosts 10.255.255.1 --timeout 500ms

✨ Feature Ideas to Level This Up

This simple CLI can turn into a full networking toolkit. Try adding:

1. Parallelism Controls

Limit concurrency with a worker pool.

2. Multiple Port Scanning

Accept a list of ports and scan all of them.

3. JSON Output

Add --json mode for scripting and automation.

4. Colourised Terminal Output

Use ANSI colours: green for OK, red for FAILED.

5. ICMP Ping Support

Requires raw sockets — a fun challenge (Go’s golang.org/x/net/icmp package helps).

6. Config File Support

Use YAML/TOML for saved scan profiles.

7. Build a TUI (Terminal UI)

Using libraries like:

  • tcell
  • bubbletea

You could make a dashboard-style updater showing live scan results.


🎉 Summary

In this tutorial, you built a fast, concurrent, networking-related CLI tool in Go — the kind of thing Go absolutely excels at.

You now have hands-on experience with:

  • Flag parsing
  • TCP networking
  • Concurrency with goroutines
  • Channels
  • Timeouts
  • Clean CLI output