Maison  >  Article  >  développement back-end  >  Apprenez étape par étape à créer un simple scanner de port TCP en utilisant le langage Go.

Apprenez étape par étape à créer un simple scanner de port TCP en utilisant le langage Go.

Go语言进阶学习
Go语言进阶学习avant
2023-07-24 15:33:201614parcourir

Essence de numérisation TCP

Ce que nous devons savoir lorsque nous utilisons TCP pour nous connecter La machine de l'autre partie ip:port

Poignée de main normale

Si la connexion réussit, le processus est le suivant.

Apprenez étape par étape à créer un simple scanner de port TCP en utilisant le langage Go.

Échec de connexion

peut être normal, ou échec Si est fermé par la partie connectée , le processus est le suivant.

Apprenez étape par étape à créer un simple scanner de port TCP en utilisant le langage Go.

S'il y a un pare-feu

il y a une autre possibilité, le port est ouvert , mais interception du pare-feu, le processus est le suivant.

Apprenez étape par étape à créer un simple scanner de port TCP en utilisant le langage Go.

Code

Après avoir compris l'essence, vous pouvez commencer à coder.

Dans Go, nous utilisons généralement net.Dial pour établir des connexions TCP.

Il n'y a que deux situations

  • Succès : Retour à Conn.

  • Échec:err != nil.

Version normale

Relativement parlant, au début, nous ne sommes peut-être pas trop audacieux, et nous écrivons tous d'abord des prototypes, sans tenir compte des performances.

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, "打开")
  }
}

Résultats d'exécution

Apprenez étape par étape à créer un simple scanner de port TCP en utilisant le langage Go.

Mais ce processus est très lent.

因为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)
}

执行结果

Apprenez étape par étape à créer un simple scanner de port TCP en utilisant le langage Go.

En fait, 6W plusieurs threads sont ouverts en même temps pour scanner chacun ip:portip:port

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

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

线程池版

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

Donc, le temps qu'il faut

le fil le plus long pour se terminer est le temps où le programme se termine.

feeling Pas mal, 60 000 ports ont été analysés en plus de 20 secondes ! ! !

🎜

🎜Version du pool de threads🎜

🎜Au-dessus de nous🎜🎜🎜 Méthode 🎜🎜🎜 simple et grossière pour chaque 🎜🎜ip:port🎜🎜 créent tous deux une coroutine. 🎜🎜🎜🎜Bien que dans Go, ce n'est théoriquement pas un problème d'ouvrir des centaines de milliers de coroutines, il y a quand même une certaine 🎜🎜🎜pression🎜🎜🎜. 🎜🎜

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


本次使用的线程池包:gohive

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

简单介绍

Apprenez étape par étape à créer un simple scanner de port TCP en utilisant le langage Go.

代码

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)
}

执行结果

Apprenez étape par étape à créer un simple scanner de port TCP en utilisant le langage Go.

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


Supposons qu'il y ait une telle demande maintenant, il y en a 100 ipip,需要扫描每个ip开放的端口,如果采用简单粗暴开线程的方式.

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

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

但是有一点的是,在Go中,线程池通常需要配合chan, besoin de scanner chaque ip

🎜Un port ouvert, si vous utilisez une manière simple et grossière d'ouvrir un fil de discussion.🎜🎜

🎜C'est tout🎜🎜🎜 🎜, plus de 600 w threads consomment encore beaucoup de mémoire et le système peut planter si la méthode du pool de threads est utilisée. 🎜🎜

🎜will Si le pool de threads est contrôlé à 500 000, la situation pourrait être bien meilleure. 🎜🎜

🎜Mais Une chose est que dans Go, le pool de threads doit généralement être coordonné 🎜🎜chan🎜🎜L'utilisation peut nécessiter une bonne base. 🎜🎜

Résumé

Cet article est plutôt un article amusant, pour en savoir plus sur trucs amusants.

En fait, vous pouvez aussi passer net.DialTimeoutnet.DialTimeout连接ip:portConnexionip:port

, ce délai d'attente peut être défini, par exemple, si le délai d'attente est de 5 s, il sera déterminé que le port n'est pas ouvert .

Je ne donnerai pas d’exemples ici. Nous utilisons principalement trois méthodes

    pour implémenter les fonctions.
  • Version normale

  • , pas de simultanéité, très lente.
  • Coroutines multiples

    🎜version, concurrence, hautes performances, mais trop de coroutines peuvent planter. 🎜🎜
  • Coroutine poolversion, concurrence, hautes performances et nombre contrôlable de coroutines.

Normalement, si les bases sont OK, il est plus recommandé d'utiliser la méthode du pool de coroutines.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer