Heim >Backend-Entwicklung >Golang >Bringen Sie Ihnen Schritt für Schritt bei, wie Sie mit der Go-Sprache einen einfachen TCP-Port-Scanner erstellen

Bringen Sie Ihnen Schritt für Schritt bei, wie Sie mit der Go-Sprache einen einfachen TCP-Port-Scanner erstellen

Go语言进阶学习
Go语言进阶学习nach vorne
2023-07-24 15:33:201682Durchsuche
... Die Maschine der anderen Partei

Normaler Handschlag

Wenn die Verbindung erfolgreich ist, ist der Vorgang wie folgt.

Verbindungsfehler

kann normal sein, oder Fehler Wenn von der verbundenen Partei geschlossen wird, ist der Vorgang wie folgt.

Bringen Sie Ihnen Schritt für Schritt bei, wie Sie mit der Go-Sprache einen einfachen TCP-Port-Scanner erstellen

Wenn es eine Firewall gibt

gibt es eine andere Möglichkeit, der Port ist offen , aber Firewall-Abfangen, Der Prozess ist wie folgt.

Bringen Sie Ihnen Schritt für Schritt bei, wie Sie mit der Go-Sprache einen einfachen TCP-Port-Scanner erstellen

Code

Nachdem Sie das Wesentliche verstanden haben, können Sie mit dem Codieren beginnen.

In Go verwenden wir normalerweise net.Dial, um TCP-Verbindungen herzustellen.

Es gibt nur zwei Situationen

  • Erfolg: Rückkehr zur Verbindung.

  • Fehler:err != nil.

Normale Version

Relativ gesehen sind wir am Anfang vielleicht nicht zu mutig und schreiben alle zuerst Prototypen, ohne die Leistung zu berücksichtigen.

Code

package main


import (
    "fmt"
    "net"
)


func main() {
    var ip = "192.168.43.34"
    for i := 21; i <= 120; i++ {
        var address = fmt.Sprintf("%s:%d", ip, i)
        conn, err := net.Dial("tcp", address)
        if err != nil {
            fmt.Println(address, "是关闭的")
            continue
        }
        conn.Close()
        fmt.Println(address, "打开")
  }
}

Ausführungsergebnisse

Bringen Sie Ihnen Schritt für Schritt bei, wie Sie mit der Go-Sprache einen einfachen TCP-Port-Scanner erstellen

Aber dieser Prozess ist sehr langsam.

因为net.Dial如果连接的是未开放的端口,一个端口可能就是20s+,所以,我们为什么学习多线程懂了把!!!

多线程版

上述是通过循环去一个个连接ip:port的,那我们就知道了,在一个个连接的位置,让多个人去干就好了。

所以,多线程如下。

代码

package main


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


func main() {


    var begin =time.Now()
    //wg
    var wg sync.WaitGroup
    //ip
    var ip = "192.168.99.112"
    //var ip = "192.168.43.34"
    //循环
    for j := 21; j <= 65535; j++ {
        //添加wg
        wg.Add(1)
        go func(i int) {
            //释放wg
            defer wg.Done()
            var address = fmt.Sprintf("%s:%d", ip, i)
            //conn, err := net.DialTimeout("tcp", address, time.Second*10)
            conn, err := net.Dial("tcp", address)
            if err != nil {
                //fmt.Println(address, "是关闭的", err)
                return
            }
            conn.Close()
            fmt.Println(address, "打开")
        }(j)
}
    //等待wg
    wg.Wait()
    var elapseTime = time.Now().Sub(begin)
    fmt.Println("耗时:", elapseTime)
}

执行结果

Bringen Sie Ihnen Schritt für Schritt bei, wie Sie mit der Go-Sprache einen einfachen TCP-Port-Scanner erstellen

Tatsächlich werden 6W mehrere Threads gleichzeitig geöffnet, um jeden zu scannen ip:portip:port

所以耗时最长的线程结束的时间,就是程序结束的时间。

感觉还行,20s+扫描完6w多个端口!!!

线程池版

上面我们简单粗暴的方式为每个ip:port.

Die Zeit, die es braucht

Der Thread, der am längsten endet , ist die Zeit, in der das Programm endet.

feeling Nicht schlecht, 60.000 Ports wurden in mehr als 20 Sekunden gescannt! ! !

🎜

🎜Thread-Pool-Version🎜

🎜Above us🎜🎜🎜 Einfache und grobe 🎜🎜🎜-Methode für jedes 🎜🎜ip:port🎜🎜 beide erstellen eine Coroutine. 🎜🎜🎜🎜Obwohl es in Go theoretisch kein Problem ist, Hunderttausende von Coroutinen zu öffnen, gibt es dennoch einen gewissen 🎜🎜🎜Druck🎜🎜🎜. 🎜🎜

所以我们应该采用一种相对节约的方式进行精简代码,一般采用线程池方式。


本次使用的线程池包:gohive

地址:https://github.com/loveleshsharma/gohive

简单介绍

Bringen Sie Ihnen Schritt für Schritt bei, wie Sie mit der Go-Sprache einen einfachen TCP-Port-Scanner erstellen

代码

package main


//线程池方式
import (
    "fmt"
    "github.com/loveleshsharma/gohive"
    "net"
    "sync"
    "time"
)


//wg
var wg sync.WaitGroup


//地址管道,100容量
var addressChan = make(chan string, 100)


//工人
func worker() {
    //函数结束释放连接
    defer wg.Done()
    for {
        address, ok := <-addressChan
        if !ok {
            break
        }
        //fmt.Println("address:", address)
        conn, err := net.Dial("tcp", address)
        //conn, err := net.DialTimeout("tcp", address, 10)
        if err != nil {
            //fmt.Println("close:", address, err)
            continue
        }
        conn.Close()
        fmt.Println("open:", address)
}
}
func main() {
    var begin = time.Now()
    //ip
    var ip = "192.168.99.112"
    //线程池大小
    var pool_size = 70000
    var pool = gohive.NewFixedSizePool(pool_size)


    //拼接ip:端口
    //启动一个线程,用于生成ip:port,并且存放到地址管道种
    go func() {
        for port := 1; port <= 65535; port++ {
            var address = fmt.Sprintf("%s:%d", ip, port)
            //将address添加到地址管道
            //fmt.Println("<-:",address)
            addressChan <- address
        }
        //发送完关闭 addressChan 管道
        close(addressChan)
}()
    //启动pool_size工人,处理addressChan种的每个地址
    for work := 0; work < pool_size; work++ {
        wg.Add(1)
        pool.Submit(worker)
}
    //等待结束
    wg.Wait()
    //计算时间
    var elapseTime = time.Now().Sub(begin)
    fmt.Println("耗时:", elapseTime)
}

执行结果

Bringen Sie Ihnen Schritt für Schritt bei, wie Sie mit der Go-Sprache einen einfachen TCP-Port-Scanner erstellen

我设置的线程池大小是7w个,所以也是一下子开启6w多个协程的,但是我们已经可以进行线程大小约束了。


Angenommen, es gibt jetzt eine solche Anfrage, es sind 100 ipip,需要扫描每个ip开放的端口,如果采用简单粗暴开线程的方式.

那就是100+65535=6552300,600多w个线程,还是比较消耗内存的,可能系统就会崩溃,如果采用线程池方式。

将线程池控制在50w个,或许情况就会好很多。

但是有一点的是,在Go中,线程池通常需要配合chan, muss jeweils gescannt werden ip

🎜Ein offener Port, wenn Sie einen Thread auf einfache und grobe Weise öffnen.🎜🎜🎜Das ist es🎜🎜100+65535=6552300🎜 🎜, mehr als 600 W-Threads verbrauchen immer noch viel Speicher und das System kann abstürzen, wenn die Thread-Pool-Methode verwendet wird. 🎜🎜

🎜will Wenn der Thread-Pool auf 500.000 gesteuert wird, ist die Situation möglicherweise viel besser. 🎜🎜

🎜But Eine Sache ist, dass in Go der Thread-Pool normalerweise mit 🎜🎜chan🎜🎜Für die Verwendung ist möglicherweise eine gute Grundlage erforderlich. 🎜🎜

Zusammenfassung

Dieser Artikel ist eher ein lustiger Artikel, um mehr über lustige Dinge zu erfahren.

Tatsächlich können Sie auch net.DialTimeoutnet.DialTimeout连接ip:portConnectionip:port

, dieses Timeout kann eingestellt werden. Wenn das Timeout beispielsweise 5 Sekunden beträgt, wird festgestellt, dass der Port nicht geöffnet ist .

Ich werde hier keine Beispiele nennen. Wir verwenden hauptsächlich drei Methoden

    , um Funktionen zu implementieren.
  • Normale

  • Version, keine Parallelität, sehr langsam.
  • Mehrere Coroutinen

    🎜Version, Parallelität, hohe Leistung, aber zu viele Coroutinen können abstürzen. 🎜🎜
  • Coroutinen-PoolVersion, Parallelität, hohe Leistung und kontrollierbare Anzahl von Coroutinen.

Wenn die Grundlagen ausreichend sind, ist es normalerweise empfehlenswerter , die Coroutine-Pool-Methode zu verwenden.

Das obige ist der detaillierte Inhalt vonBringen Sie Ihnen Schritt für Schritt bei, wie Sie mit der Go-Sprache einen einfachen TCP-Port-Scanner erstellen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:Go语言进阶学习. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen