用golang实现tcp端口扫描器

Hello World, Hello Golang

Posted by Resulte on February 22, 2021

用golang实现tcp端口扫描器

tcp端口扫描器 就是针对某一IP地址,扫描某个范围内的端口是否打开。

1. 非并发版本实现

package main

import (
	"fmt"
	"net"
)

func main() {
	ip := "11.111.10.11"
	startPort := 21
	endPort := 120
	for port := startPort; port < endPort; port++ {
		address := fmt.Sprintf("%s:%d", ip, port)
		conn, err := net.Dial("tcp", address)
		if err != nil {
			fmt.Printf("%s 关闭了\n", address)
			continue
		}
		conn.Close()
		fmt.Printf("%s 打开了\n", address)
	}
}

注:url,初始扫描端口,结束扫描端口可以自己更改

非并发下,代码执行时间特别慢。

2. 并发版本实现

并发版本使用goroutine + WaitGroup的形式。

WaitGroup本质上是一个计数器,起阻塞作用,让所有的goroutine执行完再结束。

package main

import (
	"fmt"
	"net"
	"sync"
	"time"
)

func main() {
	start := time.Now()

	var wg sync.WaitGroup

	ip := "11.111.10.11"
	startPort := 21
	endPort := 120
	for port := startPort; port < endPort; port++ {
		wg.Add(1)
		go func(j int) {
			defer wg.Done()
			address := fmt.Sprintf("%s:%d", ip, j)
			conn, err := net.Dial("tcp", address)
			if err != nil {
				fmt.Printf("%s 关闭了\n", address)
				return
			}
			conn.Close()
			fmt.Printf("%s 打开了\n", address)
		}(port)
	}

	wg.Wait()

	elapsed := time.Since(start) / 1e9
	fmt.Printf("%d seconds", elapsed)
}

3. goroutine池并发版本实现

可以使用另外一个管道results来阻塞。

package main

import (
	"fmt"
	"net"
	"time"
)

var ip = "11.111.10.11"

func worker(ports chan int, results chan int) {
	for port := range ports {
		address := fmt.Sprintf("%s:%d", ip, port)
		conn, err := net.Dial("tcp", address)
		if err != nil {
			// fmt.Printf("%s 关闭了\n", address)
			results <- 0
			continue
		}
		conn.Close()
		// fmt.Printf("%s 打开了\n", address)
		results <- port
	}
}

func main() {
	start := time.Now()

	ports := make(chan int, 100)
	results := make(chan int)
	var openPort []int

	for i := 0; i < cap(ports); i++ {
		go worker(ports, results)
	}

	startPort := 21
	endPort := 120

	go func() {
		for port := startPort; port < endPort; port++ {
			ports <- port
		}
	}()

	for p := startPort; p < endPort; p++ {
		port := <-results
		if port != 0 {
			openPort = append(openPort, port)
		}
	}

	elapsed := time.Since(start) / 1e9
	fmt.Printf("%d seconds", elapsed)
}