深入详解如何使用Go实现端口扫描器

作者:

文章目录
  • 某天深夜,我盯着路由器发呆:"嘿,我家这台小铁盒子,到底开了哪些端口?会不会有神秘服务在偷偷运行?" 于是——我决定写个工具,批量敲门: "你好,请问 1 号端口在家吗?" "2 号呢?" "……65535 号?你睡了吗?" 结果发现: 22 号(SSH)说:"密码不对,走开!" 80 号(HTTP)热情招呼:"欢迎访问我的 404 页面!" 而 1433 号(MSSQL)……根本没人应门 😢 今天,我就带你用 Go 语言,100 行代码实现一个高并发 TCP 端口扫描器,轻松探查目标主机哪些"门"是开着的!
  • 我们的程序就像一家快递公司: 老板(main):把 65535 个包裹(端口任务)放进仓库(channel)。 快递员(goroutine):100 个(或你指定的数量)工人同时取件、送货(尝试连接)。 成功送达? → 记录日志:"XX 端口有人签收!" 没人收? → 默默扔掉,继续下一件。 整个过程又快又稳,全靠 Go 的并发神器:goroutine + channel!
  • $ ./portscanner 127.0.0.1 502025-11-08 22:19:16:330076[main.go:75][INFO]开放的服务:192.168.1.1 [192.168.1.1:25]2025-11-08 22:19:16:334967[main.go:75][INFO]开放的服务:192.168.1.1 [192.168.1.1:80]2025-11-08 22:19:17:328821[main.go:75][INFO]开放的服务:192.168.1.1 [192.168.1.1:110]2025-11-08 22:19:20:333560[main.go:75][INFO]开放的服务:192.168.1.1 [192.168.1.1:443]…扫描完成。 同时,日志还会自动保存到 runlog/ 目录,方便后续分析!
  • 依赖:github.com/Jjmgx/mgxlog(一个简单的日志库) 安装命令:go get github.com/Jjmgx/mgxlog package main import ( “fmt” “net” “os” “strconv” “sync” “time” “github.com/Jjmgx/mgxlog” ) var wg sync.WaitGroup var taskCh = make(chan string, 1000) // 任务通道:ip:port var logger, _ = mgxlog.NewMgxLog(“runlog/”, 10*1024*1024, 100, 3, 1000) func main() { if len(os.Args) != 3 { fmt.Fprintf(os.Stderr, “用法: %s <IP地址> <线程数>n”, os.Args[0]) os.Exit(1) } ip := os.Args[1] threadStr := os.Args[2] // 验证 IP 格式 if net.ParseIP(ip) == nil { fmt.Fprintf(os.Stderr, “错误: 无效的 IP 地址 ‘%s’n”, ip) os.Exit(1) } // 解析线程数 threads, err := strconv.Atoi(threadStr) if err != nil || threads <= 0 || threads > 10000 { fmt.Fprintf(os.Stderr, “错误: 线程数必须是 1~10000 之间的整数n”) os.Exit(1) } // 启动 worker goroutines for i := 0; i < threads; i++ { wg.Add(1) go scanWorker() } // 生产者:生成所有端口任务(1~65535) go func() { defer close(taskCh) for port := 1; port <= 65535; port++ { taskCh <- fmt.Sprintf(“%s:%d”, ip, port) } }() wg.Wait() fmt.Println(“扫描完成。”) } func scanWorker() { defer wg.Done() for ipPort := range taskCh { scanPort(ipPort) } } func scanPort(ipPort string) { conn, err := net.DialTimeout(“tcp”, ipPort, 1*time.Second) if err != nil { return // 连不上?下一个! } defer conn.Close() addr := conn.RemoteAddr().(*net.TCPAddr) msg := fmt.Sprintf(“开放的服务:%s [%s:%d]”, addr.IP, addr.IP, addr.Port) logger.Info(msg) fmt.Println(msg) // 控制台也输出,看得爽! }
  • 这个小工具展示了 Go 在网络编程和并发处理上的强大能力。你可以在此基础上扩展: 支持端口范围(如 80,443,8000-9000) 识别服务类型(发送 HTTP/TDS/Redis 协议探测包) 输出 JSON / CSV 报告 但请记住:好奇心要有,边界感更要有。只扫描你拥有权限的设备,做一名负责任的"数字侦探"! 到此这篇关于深入详解如何使用Go实现端口扫描器的文章就介绍到这了,更多相关Go端口扫描器内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: 基于Go语言实现一个并发端口扫描器 GoLand编写 TCP 端口扫描器的详细过程 go 实现简易端口扫描的示例 go语言制作端口扫描器 Go语言实现的简单网络端口扫描方法 GO语言实现的端口扫描器分享
  • 目录
    • 一、故事从一个"好奇"开始
    • 二、核心思路:生产者 + 消费者模型
    • 三、关键 Go 知识点解析
      • 1. goroutine:轻量级线程
      • 2. channel:安全通信管道
      • 3. sync.WaitGroup:等待所有任务完成
      • 4. net.DialTimeout:带超时的 TCP 连接
      • 5. 命令行参数解析
      • 6. 错误处理 & 输入校验
    • 四、运行效果预览
      • 五、完整源码奉上!
        • 六、结语:技术无罪,滥用有责

          免责声明:本文仅供学习交流,请勿用于非 法扫描他人设备。

          某天深夜,我盯着路由器发呆:"嘿,我家这台小铁盒子,到底开了哪些端口?会不会有神秘服务在偷偷运行?"

          于是——我决定写个工具,批量敲门:

          "你好,请问 1 号端口在家吗?"

          "2 号呢?"

          "……65535 号?你睡了吗?"

          结果发现:

          22 号(SSH)说:"密码不对,走开!" 80 号(HTTP)热情招呼:"欢迎访问我的 404 页面!" 而 1433 号(MSSQL)……根本没人应门 😢 今天,我就带你用 Go 语言,100 行代码实现一个高并发 TCP 端口扫描器,轻松探查目标主机哪些"门"是开着的!

          我们的程序就像一家快递公司:

          老板(main):把 65535 个包裹(端口任务)放进仓库(channel)。 快递员(goroutine):100 个(或你指定的数量)工人同时取件、送货(尝试连接)。 成功送达? → 记录日志:"XX 端口有人签收!" 没人收? → 默默扔掉,继续下一件。 整个过程又快又稳,全靠 Go 的并发神器:goroutine + channel!

          scanWorker() // 启动一个后台任务
          
          • 不是操作系统线程,而是 Go 调度器管理的"绿色线程"。
          • 启动成本极低,100 个并发轻轻松松。

          taskCh := make(chan string, 1000)
          
          • 带缓冲的 channel,作为任务队列。
          • 多个 goroutine 安全地从中读取任务,无需加锁!

          wg.Add(1)
          defer wg.Done()
          wg.Wait()
          
          • 主 goroutine 会卡在 Wait(),直到所有 worker 执行完毕。
          • 避免程序提前退出。

          net.DialTimeout("tcp", "192.168.1.1:80", 1*time.Second)
          
          • 如果 1 秒内连不上,就放弃,避免卡死。
          • 是端口扫描的核心 API!

          ip := os.Args[1]
          threads := os.Args[2]
          
          • 简单直接,适合小型工具。
          • (复杂场景可用 flag 或 cobra 库)

          • 用 net.ParseIP 验证 IP 格式。
          • 用 strconv.Atoi 转换线程数,并检查范围。
          • 用户输错?友好提示,优雅退出。

          $ ./portscanner 127.0.0.1 50
          2025-11-08 22:19:16:330076[main.go:75][INFO]开放的服务:192.168.1.1 [192.168.1.1:25]
          2025-11-08 22:19:16:334967[main.go:75][INFO]开放的服务:192.168.1.1 [192.168.1.1:80]
          2025-11-08 22:19:17:328821[main.go:75][INFO]开放的服务:192.168.1.1 [192.168.1.1:110]
          2025-11-08 22:19:20:333560[main.go:75][INFO]开放的服务:192.168.1.1 [192.168.1.1:443]

          扫描完成。

          同时,日志还会自动保存到 runlog/ 目录,方便后续分析!

          依赖:github.com/Jjmgx/mgxlog(一个简单的日志库)

          安装命令:go get github.com/Jjmgx/mgxlog

          package main
          
          import (
          	"fmt"
          	"net"
          	"os"
          	"strconv"
          	"sync"
          	"time"
          
          	"github.com/Jjmgx/mgxlog"
          )
          
          var wg sync.WaitGroup
          var taskCh = make(chan string, 1000) // 任务通道:ip:port
          
          var logger, _ = mgxlog.NewMgxLog("runlog/", 10*1024*1024, 100, 3, 1000)
          
          func main() {
          	if len(os.Args) != 3 {
          		fmt.Fprintf(os.Stderr, "用法: %s <IP地址> <线程数>n", os.Args[0])
          		os.Exit(1)
          	}
          
          	ip := os.Args[1]
          	threadStr := os.Args[2]
          
          	// 验证 IP 格式
          	if net.ParseIP(ip) == nil {
          		fmt.Fprintf(os.Stderr, "错误: 无效的 IP 地址 '%s'n", ip)
          		os.Exit(1)
          	}
          
          	// 解析线程数
          	threads, err := strconv.Atoi(threadStr)
          	if err != nil || threads <= 0 || threads > 10000 {
          		fmt.Fprintf(os.Stderr, "错误: 线程数必须是 1~10000 之间的整数n")
          		os.Exit(1)
          	}
          
          	// 启动 worker goroutines
          	for i := 0; i < threads; i++ {
          		wg.Add(1)
          		go scanWorker()
          	}
          
          	// 生产者:生成所有端口任务(1~65535)
          	go func() {
          		defer close(taskCh)
          		for port := 1; port <= 65535; port++ {
          			taskCh <- fmt.Sprintf("%s:%d", ip, port)
          		}
          	}()
          
          	wg.Wait()
          	fmt.Println("扫描完成。")
          }
          
          func scanWorker() {
          	defer wg.Done()
          	for ipPort := range taskCh {
          		scanPort(ipPort)
          	}
          }
          
          func scanPort(ipPort string) {
          	conn, err := net.DialTimeout("tcp", ipPort, 1*time.Second)
          	if err != nil {
          		return // 连不上?下一个!
          	}
          	defer conn.Close()
          
          	addr := conn.RemoteAddr().(*net.TCPAddr)
          	msg := fmt.Sprintf("开放的服务:%s [%s:%d]", addr.IP, addr.IP, addr.Port)
          	logger.Info(msg)
          	fmt.Println(msg) // 控制台也输出,看得爽!
          }
          

          这个小工具展示了 Go 在网络编程和并发处理上的强大能力。你可以在此基础上扩展:

          • 支持端口范围(如 80,443,8000-9000)
          • 识别服务类型(发送 HTTP/TDS/Redis 协议探测包)
          • 输出 JSON / CSV 报告

          但请记住:好奇心要有,边界感更要有。只扫描你拥有权限的设备,做一名负责任的"数字侦探"!

          到此这篇关于深入详解如何使用Go实现端口扫描器的文章就介绍到这了,更多相关Go端口扫描器内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

          您可能感兴趣的文章:

          • 基于Go语言实现一个并发端口扫描器
          • GoLand编写 TCP 端口扫描器的详细过程
          • go 实现简易端口扫描的示例
          • go语言制作端口扫描器
          • Go语言实现的简单网络端口扫描方法
          • GO语言实现的端口扫描器分享

          站内搜索