0

我有一个包含以下内容的文本文件:

192.168.1.2$nick
192.168.1.3$peter
192.168.1.4$mike
192.168.1.5$joe

Web 服务器正在列表中的每个 IP 上运行。

我需要检查服务器当前是否可用,如果不可用则输出一条消息。

我写了一个小应用程序。它可以工作,但会定期产生不正确的结果——它不会为实际上不可用的服务器输出消息。

我不知道发生了什么,事实上我不确定我是否在 goroutines 中正确使用了 http.Client。

请帮帮我。

package main

import "fmt"
import "os"
import "strings"
import "io/ioutil"
import "net/http"
import "crypto/tls"
import "time"
import "strconv"

func makeGetRequest(URL string, c *http.Client) {
    resp, err := c.Get(URL)
    if err != nil {
        fmt.Println(err)
    }

    defer resp.Body.Close()

    if !((resp.StatusCode >= 200 && resp.StatusCode <= 209)) {
        fmt.Printf("%s-%d\n", URL, resp.StatusCode)
    }
}

func makeHeadRequestAsync(tasks chan string, done chan bool, c *http.Client) {

    for {
        URL := <-tasks

        if len(URL) == 0 {
            break
        }

        resp, err := c.Head(URL)
        if err != nil {
            fmt.Println(err)
            continue
        }
        defer resp.Body.Close() 
        if !((resp.StatusCode >= 200 && resp.StatusCode <= 209)) {
            makeGetRequest(URL, c) // !!! Some servers do not support HEAD requests. !!!
        }
    }

    done <- true
}

func main() {

    if len(os.Args) < 3 {
        fmt.Println("Usage: main <number of threads> <input-file>")
        os.Exit(0)
    }

    threadsNum, err := strconv.Atoi(os.Args[1]) 
    if err != nil {
        fmt.Println("Bad first parameter. Exit.")
        os.Exit(0)
    }

    http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
    client := &http.Client {
        Timeout: 30 * time.Second,
    }

    file, err := ioutil.ReadFile(os.Args[2])
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    fileLines := strings.Split(string(file), "\n") 

    tasks := make(chan string, threadsNum) 

    done := make(chan bool)

    for i := 0; i < threadsNum; i++ {
        go makeHeadRequestAsync(tasks, done, client)
    }

    for i := 0; i < len(fileLines); i++ {
        tasks <- strings.Split(string(fileLines[i]), "$")[0:1][0]
    }

    for i := 0; i < threadsNum; i++ {
        tasks <- ""
        <-done
    }
}
4

1 回答 1

0

main()函数返回时程序终止。该代码并不能确保所有 goroutine 在从 main 返回之前都已完成。

通过执行以下操作进行修复:

  • 使用sync.WaitGroup等待 goroutines 在退出程序之前完成。
  • tasks关闭时退出 goroutine 。提交所有工作后关闭任务。

这是代码:

func makeHeadRequestAsync(tasks chan string, wg *sync.WaitGroup, c *http.Client) {
    defer wg.Done()

    // for range on channel breaks when the channel is closed.
    for URL := range tasks {
        resp, err := c.Head(URL)
        if err != nil {
            fmt.Println(err)
            continue
        }
        defer resp.Body.Close()
        if !(resp.StatusCode >= 200 && resp.StatusCode <= 209) {
            makeGetRequest(URL, c) // !!! Some servers do not support HEAD requests. !!!
        }
    }

}

func main() {

    if len(os.Args) < 3 {
        fmt.Println("Usage: main <number of threads> <input-file>")
        os.Exit(0)
    }

    threadsNum, err := strconv.Atoi(os.Args[1])
    if err != nil {
        fmt.Println("Bad first parameter. Exit.")
        os.Exit(0)
    }

    http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
    client := &http.Client{
        Timeout: 30 * time.Second,
    }

    file, err := ioutil.ReadFile(os.Args[2])
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    fileLines := strings.Split(string(file), "\n")

    tasks := make(chan string)

    var wg sync.WaitGroup
    wg.Add(threadsNum)
    for i := 0; i < threadsNum; i++ {
        go makeHeadRequestAsync(tasks, &wg, client)
    }

    for i := 0; i < len(fileLines); i++ {
        tasks <- strings.Split(string(fileLines[i]), "$")[0:1][0]
    }
    close(tasks)
    wg.Wait()
}
于 2021-06-26T17:39:45.987 回答