ВЫПОЛНЕНИЕ подпрограмм в цикле — вызов функции

#go

#Вперед

Вопрос:

Описание проблемы

Я хочу отправлять одновременный вызов для каждой итерации в моем цикле for.

Текущий код

Эта программа в основном пытается найти действительный каталог на сервере

 package main

import (
    "bufio"
    "fmt"
    "net/http"
    "os"
)

func main() {
    var site string
    if len(os.Args) > 1 {
        site = os.Args[1]
    } else {
        fmt.Println("Need the host. Example > http://www.google.com/")
        fmt.Println("wfuzz http://www.google.com/ wordlist.txt")
        site = "http://www.google.com"
        os.Exit(1)
    }
    f, err := os.Open(os.Args[2])

    if err != nil {
        fmt.Println("error opening file!", err)
    }
    scanner := bufio.NewScanner(f)
    for scanner.Scan() {
        a := site   scanner.Text()
        get_page(a)
    }
    if err := scanner.Err(); err != nil {
        fmt.Println("error", err)
    }

    var input string
    fmt.Scanln(amp;input)
    fmt.Println("done")

}

func get_page(site string) string {
    a := "n"
    res, err := http.Get(site)
    if err != nil {
        fmt.Println("error:", err)
    }
    if res.StatusCode != 404 {
        fmt.Println("Page Found!!  ::", site, " :: ", res.StatusCode)
    }
    return a
}
  

Цель для достижения

Чего я хочу добиться, так это в основном отправлять параллельные вызовы цикла for, чтобы таким образом ускорить процесс и стать конечной целью текущей программы.

Ответ №1:

Используя комбинацию стандартных библиотек sync.WaitGroup и каналов Go, вы можете выполнить это без необходимости явно отслеживать количество слов в файле, из которого вы хотите прочитать.

Измените свой get_page (примечание: это не идиоматический переход к подчеркиванию пользователем в именах функций -> https://golang.org/doc/effective_go.html#names ) func, чтобы принять дополнительный канал и группу ожидания, и вы могли бы сделать что-то вроде следующего:

 package main

import (
    "bufio"
    "fmt"
    "net/http"
    "os"
    "sync"
)

func main() {
    
    //your previous code for getting the command line arguments from os.Args, then...

    //create a WaitGroup
    wg := new(sync.WaitGroup)

    //create a channel that you can write your results too
    resultChan := make(chan string)

    scanner := bufio.NewScanner(f)
    for scanner.Scan() {
        //increment the waitgroup
        wg.Add(1)
        a := site   scanner.Text()
        //call the getPage with the http target, with the result chan, and the waitgroup
        go getPage(a, resultChan, wg)
    }

    if err := scanner.Err(); err != nil {
        fmt.Println("error", err)
        //if there is an error, close the result chan so progam can exit (for more robust cancellation of potential currently running go routines, take a look at https://blog.golang.org/pipelines)
        close(resultChan)
    }

    //start another go routine to wait for all concurrent calls to get_page to complete, then close the result channel
    go func() {
        wg.Wait()
        close(resultChan)
    }()

    for result := range resultChan {
        //deal with the result in some way
        fmt.Println(result)
    }

    fmt.Println("done")
}

//side note - it is not idiomatic go to use underscores in func name
func getPage(site string, ch chan<- string, wg *sync.WaitGroup) {
    //decrement waitgroup when operation completes
    defer wg.Done()

    res, err := http.Get(site)
    if err != nil {
        ch <- fmt.Sprintf("error: %s", err)
        return
    }
    if res.StatusCode != 404 {
        ch <- fmt.Sprintf("Page Found!!  :: %s ::  %d", site, res.StatusCode)
    }
}
  

Ответ №2:

Вы можете использовать

sync.WaitGroup

Единственная проблема заключается в том, сколько одновременных вызовов вы собираетесь выполнять.

пример

если wordlist.txt содержание этого

 admin.php 
admin
administrator
administrator
  

вы знаете, что будете выполнять 4 одновременных вызова

если содержимое

 admin.php
admin/
administrator/
administrator/
administrator/
administrator/
  

вы знаете, что будете выполнять 6 одновременных вызовов.

Приведенный ниже код был создан с файлом из 3 слов

 package main

import (
    "fmt"
    "sync"

      "bufio"
   "net/http"
    "os"
)


func main(){



    var site string
    var wg sync.WaitGroup

    if len(os.Args) > 1 {
        site = os.Args[1]
    } else {
        fmt.Println("Need the host. Example > http://www.google.com/")
        fmt.Println("wfuzz http://www.google.com/ wordlist.txt")
        site = "http://www.google.com"
        os.Exit(1)
    }

    f, err := os.Open(os.Args[2])

    if err != nil {
        fmt.Println("error opening file!", err)
    }

     wg.Add(3) // TODO check the number of words in the file 


     scanner := bufio.NewScanner(f)
     for scanner.Scan() {

        a := site   scanner.Text()
        go get_page(a,amp;wg)

    }

    if err := scanner.Err(); err != nil {
        fmt.Println("error", err)
    }


   wg.Wait()
   fmt.Println("done")

}



func get_page(site string,wg *sync.WaitGroup)  string{
    a := "n"

    defer wg.Done()

    res, err := http.Get(site)
    if err != nil {
        fmt.Println("error:", err)
    }

    if res.StatusCode != 404 {
        fmt.Println("Page Found!!  ::", site, " :: ", res.StatusCode)
    }
    return a

} 
  

Комментарии:

1. Да, возникла проблема с параллелизмом, которой я раньше не испытывал… когда у нас слишком много подключений к веб-сайту, веб-сервер иногда отказывается от большего количества подключений, чем определенное количество, или даже брандмауэр отключает соединения, предоставляя ложную информацию о доступных ресурсах на сервере, но спасибо, что ваш код работает в моей лаборатории 🙂

Ответ №3:

Вы можете использовать go get_page(a) , который запускает подпрограмму для каждого слова в списке слов, чтобы установить максимальное количество одновременных вызовов, использующих рабочий пул. (https://gobyexample.com/worker-pools )

Комментарии:

1. проблема в том, что если я использую go get_page(a), что произойдет, так это то, что моя основная функция завершится до завершения цикла, другими словами, вывод только с go get_page (a) равен нулю.

2. вы можете решить это с помощью каналов, пожалуйста, ознакомьтесь с приведенным примером. (Часть с «Наконец, мы собираем все результаты работы». описание ожидает, пока все рабочие не будут выполнены)