尝试在Go中实现端口扫描程序

I recently started to learn go. The only reson for that is the goroutine thing which seem to exist only in this language (I have java background and, to be honest, won't ever completely switch to go). I wanted to implement a simple port scanner which is to find every http server (host with opened port 80) in the given network range. Here's how I am doing this:

package main

import (
    "net"
    "fmt"
    "regexp"
    "strconv"
    "time"
)

// next two functions are shamelessly copied from somewhere 

func ip2long(ipstr string) (ip uint32) {
    r := `^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})`
    reg, err := regexp.Compile(r)
    if err != nil {
        return
    }
    ips := reg.FindStringSubmatch(ipstr)
    if ips == nil {
        return
    }
    ip1, _ := strconv.Atoi(ips[1])
    ip2, _ := strconv.Atoi(ips[2])
    ip3, _ := strconv.Atoi(ips[3])
    ip4, _ := strconv.Atoi(ips[4])
    if ip1 > 255 || ip2 > 255 || ip3 > 255 || ip4 > 255 {
        return
}
    ip += uint32(ip1 * 0x1000000)
    ip += uint32(ip2 * 0x10000)
    ip += uint32(ip3 * 0x100)
    ip += uint32(ip4)
    return
}
func long2ip(ip uint32) string {
    return fmt.Sprintf("%d.%d.%d.%d", ip>>24, ip<<8>>24, ip<<16>>24, ip<<24>>24)
}

// the actual code
func main() {
    seconds := 10 // timeout
    fmt.Println(seconds) // just to see it
    timeOut := time.Duration(seconds) * time.Second // time out to pass to the DialTimeout 
    can := make(chan int) // a chan
    req := func (ip string){ // parallelized function to do requests 
    c, err := net.DialTimeout("tcp", ip+":80",timeOut) // connect to ip with given timeout
    if err == nil { // if we're connected
        fmt.Println(ip) // output the successful ip
        c.Close() // close connection
    }
    can <- 0 // tell that we're done

}

    startIp := ip2long("50.97.99.0") // starting ip
    endIp := ip2long("50.97.102.0")
    curIp := startIp // current ip

    go func(){ // a demon function ran as goroutine which listens to the chan
        count := 0 // how many ips we processed
        looper: // label to break
        for{
            <- can // wait for some goroutine to finish
            curIp++ // next ip
            count++
            go req(long2ip(curIp)) // start new goroutine
            if (curIp > endIp) { // if we've walked through the range
                fmt.Println("final")
                break looper;
            }
        }
    }()
    numGoes := 100 // number of goroutines ran at one time
    for i := 0; i < numGoes; i++{
        can <- 0 // start 100 goroutines
    }
    // standard way to make the program hung
    var input string
    fmt.Scanln(&input)
}

I hope the code is well-commented so you can see what I'm trying to do. The ip range is the range of some hosting company and I know for sure that the IP 50.97.99.189 runs http server, but the problem is that this IP never shows up in console, when I run my given code, although the host is up and ping time is around 156 ms so 10 secs is more than enough. So the question - what am I doing wrong?

Here is a slightly reworked version that is more idiomatic go.

There are shorter ways to write it, but this is probably more clear.

Logic is basically the same. I just ran it and it worked fine, printed out several ips it connected to. This version also prints why it fails, which is more for troubleshooting.

Do you still have issues running this version? If so, what error are you getting?

My version, on Play.

package main

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

// next two functions are shamelessly copied from somewhere

func ip2long(ipstr string) (uint32, error) {
    r := `^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})`
    reg, err := regexp.Compile(r)
    if err != nil {
        return 0, err
    }
    ips := reg.FindStringSubmatch(ipstr)
    if ips == nil {
        return 0, fmt.Errorf("Invalid ip address")
    }
    var ip1, ip2, ip3, ip4 int
    if ip1, err = strconv.Atoi(ips[1]); err != nil {
        return 0, err
    }
    if ip2, err = strconv.Atoi(ips[2]); err != nil {
        return 0, err
    }
    if ip3, err = strconv.Atoi(ips[3]); err != nil {
        return 0, err
    }
    if ip4, err = strconv.Atoi(ips[4]); err != nil {
        return 0, err
    }
    if ip1 > 255 || ip2 > 255 || ip3 > 255 || ip4 > 255 {
        return 0, fmt.Errorf("Invalid ip address")
    }
    ip := uint32(ip1 * 0x1000000)
    ip += uint32(ip2 * 0x10000)
    ip += uint32(ip3 * 0x100)
    ip += uint32(ip4)
    return ip, nil
}
func long2ip(ip uint32) string {
    return fmt.Sprintf("%d.%d.%d.%d", ip>>24, ip<<8>>24, ip<<16>>24, ip<<24>>24)
}

// the actual code
func main() {
    timeOut := 10 * time.Second // time out to pass to the DialTimeout
    fmt.Println("Timeout is:", timeOut)
    req := func(ip string) { // parallelized function to do requests
        c, err := net.DialTimeout("tcp", ip+":80", timeOut) // connect to ip with given timeout
        if err == nil {                                     // if we're connected
            fmt.Println(ip) // output the successful ip
            c.Close()       // close connection
        } else {
            fmt.Println("Error is:", err)
        }
    }

    startIp, err := ip2long("50.97.99.0") // starting ip
    if err != nil {
        fmt.Println(err)
        return
    }
    endIp, err := ip2long("50.97.102.0")
    if err != nil {
        fmt.Println(err)
        return
    }
    var wg sync.WaitGroup    // synchronizer for main routine to wait for spawned workers
    ips := make(chan uint32) // channel to feed ip addrs

    //spawn 100 workers
    for idx := 0; idx < 100; idx++ {
        wg.Add(1)
        go func() {
            for ip := range ips {
                req(long2ip(ip)) // perform check of ip
            }
            wg.Done()
        }()
    }

    // send ip addrs to workers to process
    for curIp := startIp; curIp <= endIp; curIp++ {
        ips <- curIp
    }
    close(ips) // signal goroutines to end
    wg.Wait()  //wait for all goroutines to complete
}