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 收集并传递任务结果

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


相关推荐
橘子师兄4 分钟前
C++AI大模型接入SDK—ChatSDK封装
开发语言·c++·人工智能·后端
@ chen11 分钟前
Spring事务 核心知识
java·后端·spring
一点技术2 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
RANCE_atttackkk2 小时前
Springboot+langchain4j的RAG检索增强生成
java·开发语言·spring boot·后端·spring·ai·ai编程
好好研究4 小时前
Spring Boot - Thymeleaf模板引擎
java·spring boot·后端·thymeleaf
爬山算法4 小时前
Hibernate(76)如何在混合持久化环境中使用Hibernate?
java·后端·hibernate
她说..4 小时前
策略模式+工厂模式实现单接口适配多审核节点
java·spring boot·后端·spring·简单工厂模式·策略模式
csdn_aspnet4 小时前
ASP.NET 8 - Cookie 身份验证
后端·asp.net·cookie·.net8
笔画人生4 小时前
Cursor + 蓝耘API:用自然语言完成全栈项目开发
前端·后端
有来技术5 小时前
ASP.NET Core 权限管理系统(RBAC)设计与实现|vue3-element-admin .NET 后端
vue.js·后端·c#·asp.net·.net