Go语言实战案例:实现一个并发端口扫描器

本篇为《Go语言100个实战案例 · 网络与并发篇》第8篇,介绍如何使用 Go 实现一个并发端口扫描器。通过 Goroutine 并发扫描多个端口,极大地提升端口扫描的效率。本文不仅讲解了如何使用 Go 的并发特性,还涉及了如何处理超时和错误,保证端口扫描的健壮性和效率。


一、实战背景

端口扫描器是网络安全领域常用的工具,它通过对目标主机的端口进行扫描,检测目标设备是否开放某些服务端口(如 HTTP、FTP、SSH 等)。常见的端口扫描场景包括:

  • 安全审计:检测主机是否存在未授权服务
  • 漏洞扫描:识别可能存在漏洞的服务端口
  • 网络诊断:排查服务器是否正常工作

传统的端口扫描使用串行方式,一个端口一个端口地检查,速度非常慢。而 Go 的并发编程可以极大地提升扫描效率。


二、实战目标

我们将实现一个并发端口扫描器,具备以下功能:

  1. 输入目标主机 IP 和端口范围
  2. 使用并发扫描多个端口
  3. 输出每个端口的开放状态
  4. 使用超时机制控制扫描时间,避免阻塞

三、完整代码实现

1. 扫描器实现(scanner.go)

go 复制代码
package main

import (
    "fmt"
    "net"
    "strconv"
    "sync"
    "time"
)

func scanPort(ip string, port int, wg *sync.WaitGroup, resultCh chan<- string) {
    defer wg.Done()

    address := fmt.Sprintf("%s:%d", ip, port)
    conn, err := net.DialTimeout("tcp", address, 1*time.Second) // 设置连接超时时间
    if err != nil {
        resultCh <- fmt.Sprintf("端口 %d: 关闭", port)
        return
    }
    conn.Close()
    resultCh <- fmt.Sprintf("端口 %d: 开放", port)
}

func main() {
    var wg sync.WaitGroup
    resultCh := make(chan string, 100) // 创建带缓冲的 Channel,缓冲区大小为 100

    fmt.Print("请输入目标 IP 地址: ")
    var ip string
    fmt.Scanln(&ip)

    fmt.Print("请输入端口范围(例如:80 100): ")
    var startPort, endPort int
    fmt.Scanln(&startPort, &endPort)

    // 扫描指定范围的端口
    for port := startPort; port <= endPort; port++ {
        wg.Add(1)
        go scanPort(ip, port, &wg, resultCh)
    }

    // 等待所有扫描任务完成并关闭 Channel
    go func() {
        wg.Wait()
        close(resultCh)
    }()

    // 打印扫描结果
    fmt.Println("\n扫描结果:")
    for result := range resultCh {
        fmt.Println(result)
    }
}

四、运行方式

执行端口扫描器

bash 复制代码
go run scanner.go

输入示例:

复制代码
请输入目标 IP 地址: 192.168.1.1
请输入端口范围(例如:80 100): 80 90

输出示例:

yaml 复制代码
扫描结果:
端口 80: 开放
端口 81: 关闭
端口 82: 关闭
端口 83: 关闭
端口 84: 开放
端口 85: 关闭
端口 86: 开放
端口 87: 关闭
端口 88: 关闭
端口 89: 开放
端口 90: 关闭

五、关键技术点解析

1. 使用 Goroutine 并发扫描端口

每个端口的扫描任务都是由独立的 Goroutine 执行的。这样可以最大化利用 CPU 核心,实现端口扫描的并发化。

go 复制代码
go scanPort(ip, port, &wg, resultCh)

2. 使用 sync.WaitGroup 等待所有 Goroutine 完成

WaitGroup 用来等待所有扫描任务完成,确保在关闭结果通道之前所有扫描任务已经结束。

go 复制代码
var wg sync.WaitGroup
wg.Add(1)

3. 使用 net.DialTimeout 设置连接超时

DialTimeout 可以在指定的超时时间内完成连接,如果超时则返回错误。我们在这里设置了 1 秒的超时。

go 复制代码
conn, err := net.DialTimeout("tcp", address, 1*time.Second)

4. 使用 channel 收集扫描结果

通过 Channel 传递每个端口的扫描结果,避免了主线程和 Goroutine 之间的数据竞争问题。

go 复制代码
resultCh <- fmt.Sprintf("端口 %d: 开放", port)

5. 缓存扫描结果并打印

使用带缓冲的 Channel resultCh 存储扫描结果。所有任务完成后,我们关闭通道并打印扫描结果。

go 复制代码
go func() {
    wg.Wait()
    close(resultCh)
}()

六、优化和扩展方向

优化方向 说明
控制并发数量 使用令牌桶算法(Token Bucket)或 sem 控制并发扫描的 Goroutine 数量
支持输入多个目标 IP 扩展支持一次扫描多个 IP 地址
扫描端口的协议支持 不仅支持 TCP 端口扫描,还可支持 UDP 扫描
日志记录 将扫描结果输出到日志文件,便于后期分析
进度条显示 添加扫描进度显示,提升用户体验
智能端口扫描 根据常见端口列表(如 80、443、21 等)进行扫描

七、小结

通过本篇案例,你学会了如何用 Go 实现一个并发端口扫描器,掌握了以下关键技术:

  • 使用 Goroutine 实现并发任务处理
  • 利用 sync.WaitGroup 等待所有并发任务完成
  • 使用 net.DialTimeout 进行超时控制,防止阻塞
  • 使用 Channel 收集并传递任务结果

这个并发端口扫描器可以帮助你在网络安全和系统维护中快速诊断目标主机的开放端口。通过优化,您还可以将其扩展成更完整的网络扫描工具。


相关推荐
陈随易10 小时前
编程语言级别的Skill市场,AI Agent 的未来形态
前端·后端·程序员
IT_陈寒13 小时前
Vite的热更新突然不香了,排查三小时差点砸键盘
前端·人工智能·后端
子兮曰13 小时前
Agency-Agents 深度解析:400+ AI 专家的"梦之队"如何重塑开发工作流
前端·后端·vibecoding
用户83562907805114 小时前
Python 实现 PDF 文件加密与解密方法
后端·python
小满zs14 小时前
Go语言第二章(小无相功)
后端·go
用户83562907805114 小时前
使用 Python 冻结与拆分 Excel 窗格教程
后端·python
karry_k14 小时前
MyBatis批量insert-select踩坑:useGeneratedKeys=true 可能让PostgreSQL返回大量插入结果
java·后端
妙码生花14 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十九):点选验证码代码逐行目检
前端·后端·go
老鹰86214 小时前
Google Wire 被官方抛弃,Uber Fx 启动就 panic,Go DI 还有救吗?
go
贰先生14 小时前
Xiuno BBS X版 用户封禁系统
后端