Golang如何加速脚本执行效率

Golang如何加速脚本执行效率 你好,我是Go语言新手,今天刚加入这个论坛。

我正在编写一个TCP扫描器,想知道有没有什么方法能大幅提升脚本的运行速度?

现在它运行得太慢了。

以下是我的脚本:

func main() {
	var hostName string
	var concurrency int

	logo()
	flag.StringVar(&hostName, "i" , "" , "Run with your hostname or IP")
	flag.IntVar(&concurrency , "c" , 50 , "Set the concurrency for greater speed")
	flag.Parse()


	var wg sync.WaitGroup
	for i:=0; i<= concurrency; i++ {
		wg.Add(1)
		go func () {
			Fullscan(hostName)
			wg.Done()
		}()
		wg.Wait()
	}
}

func Fullscan(hostName string) {
	for i := 1; i <= 65535; i++ {
		address := hostName + ":" + strconv.Itoa(i)
		conn, err := net.DialTimeout("tcp" , address , 60*time.Second)
		if err == nil {
			fmt.Println("Port" , strconv.Itoa(i)+"/open")
			conn.Close()
		}
	}
}

更多关于Golang如何加速脚本执行效率的实战教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

天哪,这在我的机器上运行得非常好,感谢你的帮助!

更多关于Golang如何加速脚本执行效率的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


哎呀,我想我明白了。谢谢。

关于工作线程,具体是什么让你感到困惑?

我会测试一下。感谢你的帮助!我正在考虑在我的VPS上运行。

我以前没有这样做过,我只是全部使用 Go 协程来写,感谢你的建议。我会尝试一下。

你好,感谢你的建议。我会尝试并研究一下,但我对工作线程(workers)有点困惑,因为我是Go语言的新手。

我刚刚意识到,你目前甚至没有为每个主机启动一个 goroutine,而是启动了 concurrency 个 goroutine,每个 goroutine 都在对同一个主机进行完整的扫描。

与其为每个主机分配一个单独的 Go 协程,不如进一步拆分。创建 10、20、40 或任意数量的“工作者”,并通过通道从主循环向它们发送作业项。该作业项应包含要扫描的主机和端口。

func main() {
    fmt.Println("hello world")
}

以下代码在我的机器上运行大约需要5秒:

package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"time"
)

type portResult struct {
	port int
	open bool
}

func main() {
	var hostName string
	var concurrency int

	flag.StringVar(&hostName, "i", "", "Give hostname or IP")
	flag.IntVar(&concurrency, "c", 10, "set number of concurrent scans")
	flag.Parse()

	var chIn chan int = make(chan int)
	var chOut chan portResult = make(chan portResult)

	for i := 0; i < concurrency; i++ {
		go runner(i, hostName, chIn, chOut)
	}

	go func() {
		for p := 1; p <= 0xffff; p++ {
			chIn <- p
		}
	}()

	for r := 1; r <= 0xffff; r++ {
		info := <-chOut

		if info.open {
			fmt.Printf("Port %v is open\n", info.port)
		}
	}

	close(chIn)
	close(chOut)
}

func runner(id int, host string, ports <-chan int, results chan<- portResult) {
	for p := range ports {
		fmt.Fprintf(os.Stderr, "%d: scanning port %v\n", id, p)
		address := fmt.Sprintf("%s:%d", host, p)
		conn, err := net.DialTimeout("tcp", address, time.Minute)
		if err != nil {
			results <- portResult{
				port: p,
				open: false,
			}
			continue
		}

		results <- portResult{
			port: p,
			open: true,
		}
		conn.Close()
	}
}
$ time go run . -i localhost -c 100 | tee ports.txt
[…]
go run . -i localhost -c 100  4.81s user 5.32s system 269% cpu 3.755 total
tee ports.txt  0.00s user 0.00s system 0% cpu 3.755 total

结果向 stdout 打印了24行,这与我机器上运行并绑定到环回接口的服务数量相符。

另外请注意,在我的机器上,并发数超过4之后,速度没有明显提升。对于并发数4、5、10和100,实际耗时都在4.5到5秒之间。

你的代码存在几个关键问题导致扫描速度慢:

  1. 并发控制错误wg.Wait()在循环内部调用,导致实际是串行执行
  2. 端口扫描逻辑:每个goroutine都扫描全部65535个端口,造成重复工作
  3. 超时时间过长:60秒超时对于端口扫描来说太长了

以下是优化后的代码:

func main() {
	var hostName string
	var concurrency int

	flag.StringVar(&hostName, "i", "", "Run with your hostname or IP")
	flag.IntVar(&concurrency, "c", 50, "Set the concurrency for greater speed")
	flag.Parse()

	if hostName == "" {
		fmt.Println("Please specify a host with -i flag")
		return
	}

	ports := make(chan int, 100)
	var wg sync.WaitGroup

	// 启动worker goroutines
	for i := 0; i < concurrency; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for port := range ports {
				scanPort(hostName, port)
			}
		}()
	}

	// 发送端口到channel
	for port := 1; port <= 65535; port++ {
		ports <- port
	}
	close(ports)

	wg.Wait()
}

func scanPort(host string, port int) {
	address := fmt.Sprintf("%s:%d", host, port)
	conn, err := net.DialTimeout("tcp", address, 2*time.Second)
	if err != nil {
		return
	}
	conn.Close()
	fmt.Printf("Port %d/open\n", port)
}

进一步优化可以使用更短的超时时间和连接复用:

func scanPortWithContext(host string, port int) {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	var d net.Dialer
	conn, err := d.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		return
	}
	conn.Close()
	fmt.Printf("Port %d/open\n", port)
}

使用sync.Pool重用连接:

var dialerPool = sync.Pool{
	New: func() interface{} {
		return &net.Dialer{
			Timeout: 500 * time.Millisecond,
		}
	},
}

func scanPortWithPool(host string, port int) {
	dialer := dialerPool.Get().(*net.Dialer)
	defer dialerPool.Put(dialer)

	conn, err := dialer.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		return
	}
	conn.Close()
	fmt.Printf("Port %d/open\n", port)
}

这些优化将显著提升扫描速度:

  1. 真正的并发执行
  2. 每个端口只扫描一次
  3. 更合理的超时时间(500ms-2s)
  4. 避免资源重复创建
回到顶部