开启掘金成长之旅!这是我参与「掘金日新计划 · 6 月更文挑战」的第 11 天,点击查看活动详情
可以通过使用goroutine和channel来实现并发扫描TCP端口。它通过遍历指定的端口范围,对每个端口启动一个goroutine进行连接尝试,如果连接成功,则将该端口发送到结果通道中。
worker函数是每个goroutine的执行函数,它尝试与指定的主机和端口建立TCP连接。如果连接成功,表示该端口是开放的,将该端口发送到结果通道中。
scanPorts函数负责启动多个worker goroutine,等待它们的执行完成,并将结果整理为排序后的开放端口列表。
在main函数中,我们指定要扫描的主机IP地址、起始端口和结束端口,并调用scanPorts函数进行扫描。最后,输出开放端口的列表。
具体实现代买示例:
package main
import (
"fmt"
"net"
"sort"
"sync"
"time"
)
func worker(host string, port int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
address := fmt.Sprintf("%s:%d", host, port)
conn, err := net.DialTimeout("tcp", address, 1*time.Second)
if err == nil {
conn.Close()
results <- port
}
}
func scanPorts(host string, startPort, endPort int) []int {
var openPorts []int
var wg sync.WaitGroup
results := make(chan int)
for port := startPort; port <= endPort; port++ {
wg.Add(1)
go worker(host, port, results, &wg)
}
go func() {
wg.Wait()
close(results)
}()
for port := range results {
openPorts = append(openPorts, port)
}
sort.Ints(openPorts)
return openPorts
}
func main() {
host := "127.0.0.1"
startPort := 1
endPort := 65535
openPorts := scanPorts(host, startPort, endPort)
fmt.Printf("Open ports on %s:\n", host)
for _, port := range openPorts {
fmt.Println(port)
}
}
当分析上述的TCP端口扫描器代码时,我们可以关注以下细节:
worker函数:
worker函数是每个goroutine的执行函数,负责尝试与指定主机和端口建立TCP连接。- 使用
net.DialTimeout函数进行连接尝试,并设置1秒的超时时间,以避免长时间等待。 - 如果连接成功,表示端口是开放的,将该端口发送到结果通道
results中。
scanPorts函数:scanPorts函数用于管理并发的端口扫描。
- 创建一个用于存储开放端口的切片
openPorts。 - 创建一个结果通道
results用于接收每个worker的扫描结果。 - 使用
sync.WaitGroup来跟踪所有worker goroutine的完成情况。 - 使用循环创建goroutine,并调用
worker函数进行端口扫描。 - 启动一个额外的goroutine来等待所有worker的完成,并在完成后关闭结果通道。
- 在
results通道中接收开放端口,并将它们添加到openPorts切片中。 - 最后,对
openPorts切片进行排序,并返回排序后的开放端口列表。
main函数:
- 在
main函数中,指定要扫描的主机IP地址、起始端口和结束端口。 - 调用
scanPorts函数进行端口扫描,并将结果存储在openPorts变量中。 - 使用
fmt.Printf和循环输出开放端口列表。
这个实现利用goroutine和channel的并发特性,可以同时扫描多个端口,提高扫描效率。通过使用sync.WaitGroup等待所有的worker goroutine完成,确保在收集结果之前等待所有的扫描操作完成。