Go语言 实现 TCP 端口扫描器

在网络安全和系统管理中,端口扫描器是一种非常有用的工具。它可以帮助我们确定目标主机上哪些端口是开放的,从而了解系统的服务和潜在的安全风险。在本文中,我们将使用 Go 语言(Golang)编写三种不同版本的 TCP 端口扫描器:非并发版、并发版和 goroutine 池并发版。

P1:非并发版 TCP 端口扫描器

非并发版的端口扫描器是最基本的实现方式。它逐个扫描目标主机的端口,速度相对较慢,但对于理解端口扫描的基本原理非常有帮助。

以下是用 Go 语言实现非并发版 TCP 端口扫描器的代码:

go 复制代码
package main

import (
    "fmt"
    "net"
)

func main() {
    targetHost := "127.0.0.1"
    for port := 1; port <= 65535; port++ {
        address := fmt.Sprintf("%s:%d", targetHost, port)
        conn, err := net.Dial("tcp", address)
        if err == nil {
            fmt.Printf("Port %d is open\n", port)
            conn.Close()
        }
    }
}

在这个版本中,我们使用了一个简单的循环来遍历从 1 到 65535 的所有端口。对于每个端口,我们尝试建立一个 TCP 连接到目标主机。如果连接成功,说明该端口是开放的,我们打印出端口号。

P2:并发版 TCP 端口扫描器

并发版的端口扫描器利用了 Go 语言的并发特性,可以同时扫描多个端口,大大提高了扫描速度。

以下是并发版 TCP 端口扫描器的代码:

go 复制代码
package main

import (
    "fmt"
    "net"
    "sync"
)

func scanPort(host string, port int, wg *sync.WaitGroup) {
    defer wg.Done()
    address := fmt.Sprintf("%s:%d", host, port)
    conn, err := net.Dial("tcp", address)
    if err == nil {
        fmt.Printf("Port %d is open\n", port)
        conn.Close()
    }
}

func main() {
    targetHost := "127.0.0.1"
    var wg sync.WaitGroup
    for port := 1; port <= 65535; port++ {
        wg.Add(1)
        go scanPort(targetHost, port, &wg)
    }
    wg.Wait()
}

在这个版本中,我们定义了一个scanPort函数,它接受目标主机和端口号作为参数,并尝试建立一个 TCP 连接。在主函数中,我们使用一个sync.WaitGroup来等待所有的 goroutine 完成。对于每个端口,我们启动一个新的 goroutine 来调用scanPort函数。

P3:goroutine 池并发版 TCP 端口扫描器

虽然并发版的端口扫描器速度很快,但如果同时启动的 goroutine 数量过多,可能会导致系统资源耗尽。为了解决这个问题,我们可以使用 goroutine 池来限制同时运行的 goroutine 数量。

以下是 goroutine 池并发版 TCP 端口扫描器的代码:

go 复制代码
package main

import (
    "fmt"
    "net"
    "sync"
)

func worker(host string, ports <-chan int, results chan<- string, wg *sync.WaitGroup) {
    defer wg.Done()
    for port := range ports {
        address := fmt.Sprintf("%s:%d", host, port)
        conn, err := net.Dial("tcp", address)
        if err == nil {
            results <- fmt.Sprintf("Port %d is open", port)
            conn.Close()
        } else {
            results <- ""
        }
    }
}

func main() {
    targetHost := "127.0.0.1"
    numWorkers := 100
    ports := make(chan int, 65535)
    results := make(chan string, 65535)
    var wg sync.WaitGroup

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go worker(targetHost, ports, results, &wg)
    }

    for port := 1; port <= 65535; port++ {
        ports <- port
    }
    close(ports)

    go func() {
        wg.Wait()
        close(results)
    }()

    for result := range results {
        if result!= "" {
            fmt.Println(result)
        }
    }
}

在这个版本中,我们创建了一个固定数量的 goroutine(即 goroutine 池)来扫描端口。我们使用两个通道:一个用于传递端口号给 worker goroutine,另一个用于接收扫描结果。在主函数中,我们启动多个 worker goroutine,然后将端口号发送到端口通道。worker goroutine 从端口通道中接收端口号,进行扫描,并将结果发送到结果通道。最后,我们从结果通道中读取并打印开放的端口号。

以下是对三种 Go 语言编写的 TCP 端口扫描器的原理介绍:

P1:非并发版 TCP 端口扫描器

原理:

  • 循环遍历从 1 到 65535 的所有端口号。对于每个端口号,通过net.Dial("tcp", address)尝试与目标主机建立 TCP 连接。
  • 如果连接成功,说明该端口处于开放状态,打印出"Port [端口号] is open"的信息,然后关闭连接。
  • 逐个端口进行尝试,不使用并发机制,速度相对较慢,因为在测试完一个端口后才会测试下一个端口。

P2:并发版 TCP 端口扫描器

原理:

  • 定义了一个scanPort函数,该函数接受目标主机和端口号作为参数。在函数内部,尝试建立 TCP 连接,如果连接成功,说明端口开放,打印端口号并关闭连接。
  • 在主函数中,使用sync.WaitGroup来协调多个 goroutine 的执行。对于每个端口号,启动一个新的 goroutine 来执行scanPort函数。
  • 通过同时启动多个 goroutine 来并发地扫描不同的端口,大大提高了扫描速度。每个 goroutine 独立地尝试连接目标主机的一个端口,互不干扰。

P3:goroutine 池并发版 TCP 端口扫描器

原理:

  • 首先创建了两个通道,一个用于传递端口号给 worker goroutine(ports通道),另一个用于接收扫描结果(results通道)。
  • 定义了一个worker函数,作为 goroutine 池中的工作函数。每个 worker goroutine 从ports通道中接收端口号,尝试建立 TCP 连接,如果连接成功,将结果("Port [端口号] is open")发送到results通道,否则发送空字符串。
  • 在主函数中,启动固定数量的 worker goroutine(即创建 goroutine 池),然后将端口号逐个发送到ports通道。
  • 当所有端口号都发送完毕后,关闭ports通道。同时,启动一个新的 goroutine 来等待所有 worker goroutine 完成任务(通过sync.WaitGroup),并在所有任务完成后关闭results通道。
  • 最后,从results通道中读取结果并打印开放的端口号。
  • 这种方式通过限制同时运行的 goroutine 数量,避免了无限制并发可能导致的系统资源耗尽问题,同时仍然能够利用并发来提高扫描速度。

通过以上三种版本的 TCP 端口扫描器,我们可以看到 Go 语言的并发特性在提高程序性能方面的强大之处。在实际应用中,我们可以根据具体需求选择合适的版本。同时,需要注意的是,在进行端口扫描时,应该遵守法律法规,确保获得合法的授权。

相关推荐
Rysxt_4 小时前
UDP请求解析教程:深入理解请求头、请求体与参数机制
网络·网络协议·udp
岛屿旅人5 小时前
英国国防部推进本土化开放架构建设
网络·人工智能·安全·web安全·架构
Unstoppable225 小时前
八股训练营第 6 天 | HTTPS 和HTTP 有哪些区别?HTTPS的工作原理(HTTPS建立连接的过程)?TCP和UDP的区别?
tcp/ip·http·https·八股
重生之我在20年代敲代码5 小时前
【Linux网络编程】初识网络,理解TCP/IP五层模型
linux·运维·服务器·网络
称心-如意5 小时前
浅谈TCP与UDP协议:TCP和UDP分别是什么,区别在哪里
网络协议·tcp/ip·udp
努力学习的小廉6 小时前
深入了解linux网络—— 守护进程
linux·运维·网络
我是Feri7 小时前
HarmonyOS6.0开发实战:HTTP 网络请求与 API 交互全指南
网络·http·harmonyos·openharmonyos·harmonyos6.0
HaiLang_IT7 小时前
2026届 网络与信息安全专业毕业设计选题推荐与指导(含热门研究方向)
网络·安全·信息安全
集大周杰伦7 小时前
Linux网络编程核心实践:TCP/UDP socket与epoll高并发服务器构建
linux·tcp/ip·网络编程·socket·字节序·套接字·i/o多路复用
写点什么吧7 小时前
网络原理-进阶
网络