编写端口扫描遇到的问题
最近在学习并发编程就尝试着写个端口扫描器(PS:仅用于学习用途)
Q(修订1.1):
- 我想把扫描可用的端口保存到
opening.txt
中,所以在case语句中写了个保存并写入的操作,但是执行起来程序会阻塞,这是什么原因,是文件资源没有关闭?后面跟了return难道不能结束 - 就是最佳答案修改后的代码中的疑惑,
isOpen()
中没有实例化chan
,为什么在main中需要进行实例化,定义之后不是可以直接用吗? - 最后就是,为什么实例化chan的代码片段在isOpen()之前,这样循环出来得到的chan是空的吧,不应该是放在
isOpen()
之后等拿到了open端口再range这个chan中的数据吗?请指教
Code
package main
import (
"context"
"fmt"
"log"
"net"
"os"
"sync"
"time"
)
var useful chan string
func main() {
var wg sync.WaitGroup
url := "127.0.0.1"
from := 3305
to := 3306
// 【问题1】 如果时间太短,无法完成 TCP 请求
// var duration time.Duration = 200 // 默认 纳秒单位
var duration = 2 * time.Second // 设置 2s
// 【问题2】 chanel 没有初始化,无法正常使用
useful = make(chan string, to-from+1) // 创建 chan 缓冲区
defer close(useful)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 【补充1】可以获取 channel 中 ,可连接的端口
go func() {
for result := range useful {
fmt.Println(result)
}
}()
for i := from; i <= to; i++ {
addr := fmt.Sprintf("%s:%d", url, i)
wg.Add(1)
go func(ctx2 context.Context) {
isOpen(ctx2, addr, duration)
wg.Done()
return
}(ctx)
// 【问题3】 如果在这里 wait 那么将是依次执行 goroutinue ,无法利用到 并发请求
// wg.Wait()
}
// 【问题3】 放到这里,则是并发请求,等待所有 goroutinue 结束,但是结果会乱序
wg.Wait()
}
func isOpen(ctx context.Context, addr string, duration time.Duration) {
conn, err := net.DialTimeout("tcp", addr, duration)
if err != nil {
log.Printf("port %s is closed. err:%s duration:%s", addr, err, duration)
return
}
defer func(conn net.Conn) {
err := conn.Close()
if err != nil {
return
}
}(conn)
select {
case <-ctx.Done():
fmt.Println("over...")
return
case useful <- addr:
log.Printf("port %s is openning", addr)
err := os.WriteFile("opening.txt", []byte(<-useful), os.ModePerm)
if err != nil {
return
}
return
}
}