文章标题 原创 翻译 转载 文章内容 通过端口扫描我们可以知道服务器上哪些端口是处于监听状态。 借助Go net模块的DialTimeout方法可以很容易的判断端口是否打开,同时对于批量端口的扫描使用Go Routines实现非常简单。 # 主结构 ``` type PortScanner struct { ip string lock *semaphore.Weighted } ``` 需要传入ip地址和一个信号量来限制goroutine启动的数量,goroutine太多会占用太多内存,对于一个小工具而言不太合适。 # 扫描单个端口 复杂一点你可能需要判断一下错误信息再重复,这里简单实现也能满足基本需求 ``` func (ps PortScanner) Scan(port int, timeout time.Duration) error { address := fmt.Sprintf("%s:%d", ps.ip, port) conn, err := net.DialTimeout("tcp", address, timeout) if err != nil { return err } conn.Close() return nil } ``` # 批量扫描端口 指定起始端口和结束端口,使用sync.WaitGroup等待函数执行完成,lock限制goroutine启动的数量 ``` func (ps PortScanner) Start(from, to int, timeout time.Duration) { wg := sync.WaitGroup{} defer wg.Wait() fmt.Println("Scanning ip", ps.ip) if to == 0 { to = from + 1 } for port := from; port < to; port++ { ps.lock.Acquire(context.TODO(), 1) wg.Add(1) go func(port int) { defer ps.lock.Release(1) defer wg.Done() start := time.Now() err := ps.Scan(port, timeout) if err == nil { fmt.Println("Port", port, "open, time", time.Since(start).Milliseconds(), "ms") } else { fmt.Println("Port", port, "closed") } }(port) } } ``` # 完整代码 ``` package main import ( "context" "fmt" "net" "os" "strconv" "strings" "sync" "time" "golang.org/x/sync/semaphore" ) type PortScanner struct { ip string lock *semaphore.Weighted } func (ps PortScanner) Scan(port int, timeout time.Duration) error { address := fmt.Sprintf("%s:%d", ps.ip, port) conn, err := net.DialTimeout("tcp", address, timeout) if err != nil { return err } conn.Close() return nil } func (ps PortScanner) Start(from, to int, timeout time.Duration) { wg := sync.WaitGroup{} defer wg.Wait() fmt.Println("Scanning ip", ps.ip) if to == 0 { to = from + 1 } for port := from; port < to; port++ { ps.lock.Acquire(context.TODO(), 1) wg.Add(1) go func(port int) { defer ps.lock.Release(1) defer wg.Done() start := time.Now() err := ps.Scan(port, timeout) if err == nil { fmt.Println("Port", port, "open, time", time.Since(start).Milliseconds(), "ms") } else { fmt.Println("Port", port, "closed") } }(port) } } func main() { helps := []string{} helps = append(helps, "\r\ncommand error, eg:") helps = append(helps, "1. portscanner 127.0.0.1:3000") helps = append(helps, "2. portscanner 127.0.0.1:1/65536") tips := strings.Join(helps, "\r\n") if len(os.Args) != 2 { panic(tips) } address := os.Args[1] s := strings.Split(address, ":") if len(s) != 2 { panic(tips) } ip := s[0] s = strings.Split(s[1], "/") var from, to int if len(s) > 0 { from, _ = strconv.Atoi(s[0]) } if len(s) > 1 { to, _ = strconv.Atoi(s[1]) } ps := PortScanner{ ip: ip, lock: semaphore.NewWeighted(100), } ps.Start(from, to, 1*time.Second) } ``` 有两个设置需要注意: * timeout,超时时间1秒 * NewWeighted,100最多同时运行的goroutine数量 为了提高扫描速度可以减少timeout,增加weighted值。 # 使用 扫描单个端口 ``` C:\Windows\system32>portscanner 127.0.0.1:135 Scanning ip 127.0.0.1 Port 135 open, time 3 ms ``` 指定扫描的端口范围 ``` C:\Windows\system32>portscanner 127.0.0.1:130/140 Scanning ip 127.0.0.1 Port 135 open, time 1 ms Port 130 closed Port 134 closed Port 132 closed Port 131 closed Port 139 closed Port 133 closed Port 138 closed Port 137 closed Port 136 closed ``` 文章类别 Python Mobile Android Java Shell Life Database Bug Windows IOS Tools Boost Node.js Mac Product Tips C/C++ Golang Javascript React Qt MQ MongoDB Design Web Linux LLM ChatGPT RAG AI 提交