用Go语言打造高效TCP扫描器:从入门到并发优化你是否好奇网络端口是如何被探测的?或者想亲手打造一个工具来扫描服务器的开放端口?Go语言以其简洁的语法和强大的并发能力,成为实现网络工具的绝佳选择。在这篇文章中,我们将从TCP三次握手的基础讲起,手把手带你用Go编写一个TCP扫
你是否好奇网络端口是如何被探测的?或者想亲手打造一个工具来扫描服务器的开放端口?Go 语言以其简洁的语法和强大的并发能力,成为实现网络工具的绝佳选择。在这篇文章中,我们将从 TCP 三次握手的基础讲起,手把手带你用 Go 编写一个 TCP 扫描器,从简单的顺序扫描,到利用 goroutine 的并发版本,再到优化后的 worker 池模型。无论你是网络编程新手还是 Go 语言爱好者,这篇干货都能让你有所收获。准备好了吗?让我们一起用代码探索网络世界!
本文详细介绍了如何用 Go 语言实现 TCP 扫描器。开篇解释了 TCP 协议和三次握手的基本原理,随后通过代码展示了非并发版本的扫描器实现。接着,文章引入 goroutine 实现并发扫描,并通过 sync.WaitGroup 提升效率。为了进一步优化,展示了基于 worker 池的并发模型,利用通道(channel)管理任务分发和结果收集,最后优化为排序输出开放和关闭端口。代码由浅入深,注释清晰,适合学习 Go 并发编程和网络开发的读者。通过本文,你将掌握 Go 在网络工具开发中的核心技巧。
客户端 -> 端口打开 ->服务器
创建目录并在该目录创建main.go 文件
~/Code/go via 🐹 v1.20.3 via 🅒 base
➜ mcd tcp-scanner
Code/go/tcp-scanner via 🐹 v1.20.3 via 🅒 base
➜ go mod init
go: cannot determine module path for source directory /Users/qiaopengjun/Code/go/tcp-scanner (outside GOPATH, module path must be specified)
Example usage:
'go mod init example.com/m' to initialize a v0 or v1 module
'go mod init example.com/m/v2' to initialize a v2 module
Run 'go help mod init' for more information.
Code/go/tcp-scanner via 🐹 v1.20.3 via 🅒 base
➜ go mod init tcp-scanner
go: creating new go.mod: module tcp-scanner
Code/go/tcp-scanner via 🐹 v1.20.3 via 🅒 base
➜ c
Code/go/tcp-scanner via 🐹 v1.20.3 via 🅒 base
➜
main.go 文件
package main
import (
"fmt"
"net"
)
func main() {
for i := 21; i < 120; i++ {
address := fmt.Sprintf("20.194.168.28:%d", i)
conn, err := net.Dial("tcp", address)
if err != nil {
fmt.Printf("%s failed 关闭了\n", address)
continue
}
conn.Close()
fmt.Printf("%s connected 打开了!!!\n", address)
}
}
package main
import (
"fmt"
"net"
"sync"
"time"
)
func main() {
start := time.Now()
var wg sync.WaitGroup
for i := 21; i < 120; i++ {
wg.Add(1)
go func(j int) {
defer wg.Done()
address := fmt.Sprintf("20.194.168.28:%d", j)
conn, err := net.Dial("tcp", address)
if err != nil {
fmt.Printf("%s 关闭了\n", address)
return
}
conn.Close()
fmt.Printf("%s 打开了!!!\n", address)
}(i)
}
wg.Wait()
elapsed := time.Since(start) / 1e9
fmt.Printf("\n\n%d seconds", elapsed)
}
// func main() {
// for i := 21; i < 120; i++ {
// address := fmt.Sprintf("20.194.168.28:%d", i)
// conn, err := net.Dial("tcp", address)
// if err != nil {
// fmt.Printf("%s failed 关闭了\n", address)
// continue
// }
// conn.Close()
// fmt.Printf("%s connected 打开了!!!\n", address)
// }
// }
package main
import (
"fmt"
"sync"
)
func worker(ports chan int, wg *sync.WaitGroup) {
for p := range ports {
fmt.Println("p", p)
wg.Done()
}
}
func main() {
ports := make(chan int, 100)
var wg sync.WaitGroup
for i := 0; i < cap(ports); i++ {
go worker(ports, &wg)
}
for i := 1; i < 1024; i++ {
wg.Add(1)
ports <- i
}
wg.Wait()
close(ports)
}
// func main() {
// start := time.Now()
// var wg sync.WaitGroup
// for i := 21; i < 120; i++ {
// wg.Add(1)
// go func(j int) {
// defer wg.Done()
// address := fmt.Sprintf("20.194.168.28:%d", j)
// conn, err := net.Dial("tcp", address)
// if err != nil {
// fmt.Printf("%s 关闭了\n", address)
// return
// }
// conn.Close()
// fmt.Printf("%s 打开了!!!\n", address)
// }(i)
// }
// wg.Wait()
// elapsed := time.Since(start) / 1e9
// fmt.Printf("\n\n%d seconds", elapsed)
// }
// func main() {
// for i := 21; i < 120; i++ {
// address := fmt.Sprintf("20.194.168.28:%d", i)
// conn, err := net.Dial("tcp", address)
// if err != nil {
// fmt.Printf("%s failed 关闭了\n", address)
// continue
// }
// conn.Close()
// fmt.Printf("%s connected 打开了!!!\n", address)
// }
// }
优化之后
package main
import (
"fmt"
"net"
"sort"
)
func worker(ports chan int, results chan int) {
for p := range ports {
address := fmt.Sprintf("20.194.168.28:%d", p)
conn, err := net.Dial("tcp", address)
if err != nil {
results <- 0
continue
}
conn.Close()
results <- p
}
}
func main() {
ports := make(chan int, 100)
results := make(chan int)
var openports []int
var closeports []int
for i := 0; i < cap(ports); i++ {
go worker(ports, results)
}
go func() {
for i := 1; i < 1024; i++ {
ports <- i
}
}()
for i := 1; i < 1024; i++ {
port := <-results
if port != 0 {
openports = append(openports, port)
} else {
closeports = append(closeports, port)
}
}
close(ports)
close(results)
sort.Ints(openports)
sort.Ints(closeports)
for _, port := range closeports {
fmt.Printf("%d closed\n", port)
}
for _, port := range openports {
fmt.Printf("%d opened\n", port)
}
}
// func main() {
// start := time.Now()
// var wg sync.WaitGroup
// for i := 21; i < 120; i++ {
// wg.Add(1)
// go func(j int) {
// defer wg.Done()
// address := fmt.Sprintf("20.194.168.28:%d", j)
// conn, err := net.Dial("tcp", address)
// if err != nil {
// fmt.Printf("%s 关闭了\n", address)
// return
// }
// conn.Close()
// fmt.Printf("%s 打开了!!!\n", address)
// }(i)
// }
// wg.Wait()
// elapsed := time.Since(start) / 1e9
// fmt.Printf("\n\n%d seconds", elapsed)
// }
// func main() {
// for i := 21; i < 120; i++ {
// address := fmt.Sprintf("20.194.168.28:%d", i)
// conn, err := net.Dial("tcp", address)
// if err != nil {
// fmt.Printf("%s failed 关闭了\n", address)
// continue
// }
// conn.Close()
// fmt.Printf("%s connected 打开了!!!\n", address)
// }
// }
通过这篇文章,我们从 TCP 的基础知识出发,用 Go 语言逐步构建了一个功能强大的 TCP 扫描器。从简单的顺序扫描,到 goroutine 的并发加速,再到 worker 池的优化设计,Go 的并发原语(如 goroutine 和 channel)让网络编程变得高效而优雅。无论你是想深入理解 TCP 协议,还是提升 Go 编程能力,这个扫描器的实现过程都为你提供了宝贵的实践经验。现在,试着运行代码,或者挑战自己扫描其他服务器吧!用 Go 探索网络的奥秘,你准备好了吗?
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!