# Simple Concurrent Port Scanner in Go

When learning network programming or cybersecurity basics, building a port scanner is a classic, hands-on project. It’s a practical way to explore sockets, concurrency, and command-line interface (CLI) development. In this post, we’ll break down a lightweight port scanner implemented in Go, showing how its concurrency model makes large-scale scanning both simple and fast.

## What is a Port Scanner?

A **port scanner** is a tool that probes a target machine for open ports. Each open port could represent a running service, like SSH (22), HTTP (80), or custom applications. Port scanning is essential for network diagnostics and is foundational in security assessments.

## Why Go for Port Scanning?

Go (or Golang) offers unique advantages for this task:

* **Goroutines** enable lightweight concurrent execution.
    
* **Channels and WaitGroups** help coordinate parallel tasks with ease.
    
* Robust networking support built into the standard library.
    

These features allow us to write highly scalable port scanners with minimal code.

## Breaking Down the Port Scanner

Let's examine the most important parts and why they matter.

## 1\. Project Structure

* **main.go**: Entry point. Sets up flags, concurrency, and result reporting.
    
* **scanner/scanner.go**: Contains the scanning logic.
    

## 2\. Command-Line Flags

The user can specify a target host via the `-host` flag (defaults to [`localhost`](http://localhost)). The scanner covers the entire range of possible TCP ports (1 to 65535), but you could easily extend this.

```plaintext
gohost := flag.String("host", "localhost", "Target host to scan")
flag.Parse()
```

## 3\. Concurrency with Goroutines and WaitGroups

The heart of the scanner is a loop that kicks off a goroutine ("lightweight thread") for each port. All goroutines share a common WaitGroup for graceful shutdown and a channel to report results.

```plaintext
gofor port := startPort; port <= endPort; port++ {
    wg.Add(1)
    go func(p int) {
        ps.CheckPort(p, openPorts, &wg)
    }(port)
}
```

## 4\. The Port Checking Logic

Under the hood, `CheckPort` tries to open a TCP connection to the target host and port.

```plaintext
goaddress := net.JoinHostPort(ps.Host, strconv.Itoa(port))
conn, err := net.DialTimeout("tcp", address, 1*time.Second)
if err == nil {
    conn.Close()
    ch <- port // Report open ports over the channel
}
```

A timeout ensures the scanner doesn’t hang on unresponsive ports.

## 5\. Collecting Results Asynchronously

As each scanner goroutine discovers open ports, it sends the port number to the main goroutine via a channel. The main code reads from this channel and prints results as they come:

```plaintext
gofor port := range openPorts {
    fmt.Printf("Port %d is open\n", port)
}
```

The result: you see open ports displayed live as they’re found, even while other scans continue in the background.

## Why This Pattern Rocks

* **High Performance:** Thanks to Go’s concurrency primitives, you can scan all 65,535 ports very quickly (although in practice, for network friendliness, you may want to limit concurrency in real-world use).
    
* **Simplicity:** The code is short, clear, and idiomatic; robust error handling and CLI parsing are easy to add.
    
* **Extensible:** You can easily add features like custom port ranges, configurable timeouts, or service banner grabbing.
    

## Possible Enhancements

* **Limit concurrent goroutines** to avoid overwhelming the host or your own machine.
    
* Support for **port ranges** or port list input instead of always scanning all ports.
    
* Add **result output in formats** like JSON or CSV.
    
* Try **UDP scanning** by using `"udp"` with `net.DialTimeout`.
    
* Include **verbose logging** for closed/filtered ports, if desired.
    

## Final Thoughts

This simple scanner highlights Go’s power for network programming. With just a couple dozen lines, we get a highly concurrent, responsive tool that’s a great starting point for anyone looking to learn about sockets, protocols, or Go’s concurrency story.

**Try extending the code—experiment with new features, or run it against test servers in your environment!**

**Sample Usage:**

```plaintext
bashgo run main.go -host example.com
```

You’ll see output like:

```plaintext
textPort 22 is open
Port 80 is open
Port 443 is open
```

Happy scanning—use responsibly
