go - 简单的工作池

问题描述

作为一个 Go 和编程的初学者,我一直在用 gopacket 库编写一个端口扫描器,大部分代码已经完成,但是我遇到了一个问题,即产生过多的 goroutines获取'read ip4 0.0.0.0: I/O timeout' 我做了一些研究,看来我需要实现一个工作池我一直在尝试实现这个例子 'https://gobyexample.com/worker-pools'因为我还在学习 goroutines 和渠道,我已经研究了几天,似乎无法弄清楚如何在我的程序中正确实现上述示例,你们能给我一些指示,或者最好是代码修复示例.

package main

import (
    "fmt"
    "log"
    "net"
    "time"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
)

// Get preferred outbound ip and port of this machine
func GetoutboundIPPort() (net.IP,int) {
    conn,err := net.Dial("udp","1.1.1.1:80")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    localAddr := conn.LocalAddr().(*net.UDPAddr)
    return localAddr.IP,localAddr.Port
}

func ipv4_gen(out chan net.IP) {
    ip,ipnet,err := net.ParseCIDR("192.168.0.0/24")
    if err != nil {
        log.Fatal(err)
    }
    for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
        time.Sleep(300 * time.Millisecond)
        dstaddrs,err := net.LookupIP(ip.String())
        if err != nil {
            log.Fatal(err)
        }
        dstip := dstaddrs[0].To4()
        out <- dstip
    }
    close(out)
}

func inc(ip net.IP) {
    for j := len(ip) - 1; j >= 0; j-- {
        ip[j]++
        if ip[j] > 0 {
            break
        }
    }
}

func port_scanner(dstip net.IP) {
    dstport := layers.TCPPort(80)

    srcip,port := GetoutboundIPPort()
    srcport := layers.TCPPort(port)

    ip := &layers.IPv4{
        SrcIP:    srcip,DstIP:    dstip,Protocol: layers.IPProtocolTCP,}
    tcp := &layers.TCP{
        SrcPort: srcport,DstPort: dstport,Seq:     1105024978,SYN:     true,Window:  14600,}
    tcp.SetNetworkLayerForChecksum(ip)
    buf := gopacket.NewSerializeBuffer()
    opts := gopacket.SerializeOptions{
        ComputeChecksums: true,FixLengths:       true,}
    if err := gopacket.SerializeLayers(buf,opts,tcp); err != nil {
        log.Fatal(err)
    }

    conn,err := net.ListenPacket("ip4:tcp","0.0.0.0")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
    if _,err := conn.Writeto(buf.Bytes(),&net.IPAddr{IP: dstip}); err != nil {
        log.Fatal(err)
    }

    // Set deadline so we don't wait forever.
    if err := conn.SetDeadline(time.Now().Add(10 * time.Second)); err != nil {
        log.Fatal(err)
    }

    for {
        b := make([]byte,4096)
        n,addr,err := conn.ReadFrom(b)
        if err != nil {
            log.Println("error reading packet: ",err)
            return
        } else if addr.String() == dstip.String() {
            // Decode a packet
            packet := gopacket.NewPacket(b[:n],layers.LayerTypeTCP,gopacket.Default)
            // Get the TCP layer from this packet
            if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
                tcp,_ := tcpLayer.(*layers.TCP)

                if tcp.DstPort == srcport {
                    if tcp.SYN && tcp.ACK {
                        fmt.Printf("discovered open port %d/tcp on %s\n",dstport,dstip)
                    }
                    return
                }
            }
        }
    }
}

func worker(id int,ips <-chan net.IP) {
    for ip := range ips {
        go port_scanner(ip)
    }
}

func main() {
    ips := make(chan net.IP)
    go ipv4_gen(ips)

    for w := 1; w <= 10; w++ {
        go worker(w,ips)
    }
}

解决方法

你正在为 worker 内部的每一项工作启动一个新的 goroutine,因此它违背了它的目的。
您需要运行工作而不是在工作器内部启动 goroutine。