Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Ajar anda langkah demi langkah cara membina pengimbas port TCP mudah menggunakan bahasa Go

Ajar anda langkah demi langkah cara membina pengimbas port TCP mudah menggunakan bahasa Go

Go语言进阶学习
Go语言进阶学习ke hadapan
2023-07-24 15:33:201614semak imbas

Intipati pengimbasan TCP

Kita perlu tahu apabila menggunakan Mesin pihak lain milikip:port

Salaman biasa

Jika sambungan berjaya, prosesnya adalah seperti berikut.

Ajar anda langkah demi langkah cara membina pengimbas port TCP mudah menggunakan bahasa Go

🎜

Kegagalan sambungan

boleh menjadi perkara biasa, atau gagal jika ditutup oleh pihak yang bersambung , prosesnya seperti berikut. . proses adalah seperti berikut.

Ajar anda langkah demi langkah cara membina pengimbas port TCP mudah menggunakan bahasa Go

Code

Selepas anda faham intipati, anda boleh mula coding. Dalam Go, biasanya kami menggunakan untuk membuat sambungan TCP

. Ajar anda langkah demi langkah cara membina pengimbas port TCP mudah menggunakan bahasa Go

Hanya ada dua situasi

  • Kejayaan: Kembali ke conn.

  • Kegagalan:err != nil.

Versi biasa

Secara relatifnya, pada mulanya, kita mungkin tidak terlalu berani, dan kita semua menulis prototaip terlebih dahulu, tanpa mengambil kira prestasi.

Kod

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

Hasil pelaksanaan

Ajar anda langkah demi langkah cara membina pengimbas port TCP mudah menggunakan bahasa Go

sangat lambat

🎜

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

执行结果

Ajar anda langkah demi langkah cara membina pengimbas port TCP mudah menggunakan bahasa Go

Malah, berbilang benang 6W dibuka pada masa yang sama untuk mengimbas setiap ip:portip:port

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

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

线程池版

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

Jadi masa yang diambil

benang paling panjang untuk tamat ialah masa program tamat.

feeling Tidak buruk, 60,000 port telah diimbas dalam 20s+! ! !

🎜

🎜Versi kumpulan benang🎜

🎜Di atas kami🎜🎜🎜 Kaedah 🎜🎜🎜 yang ringkas dan kasar untuk setiap 🎜🎜ip:port🎜🎜 kedua-duanya mencipta coroutine. 🎜🎜🎜🎜Walaupun dalam Go, secara teorinya tiada masalah untuk membuka ratusan ribu coroutine, masih ada 🎜🎜🎜tekanan🎜🎜🎜. 🎜🎜

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


本次使用的线程池包:gohive

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

简单介绍

Ajar anda langkah demi langkah cara membina pengimbas port TCP mudah menggunakan bahasa 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)
}

执行结果

Ajar anda langkah demi langkah cara membina pengimbas port TCP mudah menggunakan bahasa Go

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


Andaikan ada permintaan sedemikian sekarang, terdapat 100 ipip,需要扫描每个ip开放的端口,如果采用简单粗暴开线程的方式.

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

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

但是有一点的是,在Go中,线程池通常需要配合chan, perlu mengimbas setiap satu ip

🎜Satu port terbuka, jika anda menggunakan cara yang mudah dan kasar untuk membuka benang.🎜🎜

🎜Itu sahaja🎜🎜100+65535=6552300🎜 🎜, lebih daripada 600 w benang masih menggunakan banyak memori, dan sistem mungkin ranap jika kaedah kumpulan benang digunakan. 🎜🎜

🎜akan Jika kumpulan benang dikawal pada 500,000, keadaan mungkin lebih baik. 🎜🎜

🎜Tetapi Satu perkara ialah dalam Go, kumpulan benang biasanya perlu diselaraskan dengan 🎜🎜chan🎜🎜Penggunaan mungkin memerlukan asas yang baik. 🎜🎜.

Malah, anda juga boleh melepasi

net.DialTimeoutConnectionip:port, tamat masa ini boleh ditetapkan, contohnya, jika tamat masa ialah 5s, ia akan ditentukan bahawa port tidak dibuka . Saya tidak akan memberikan contoh di sini.

Kami terutamanya menggunakan

tiga kaedahnet.DialTimeout连接ip:port untuk melaksanakan fungsi.

Versi biasa

, tiada konkurensi, sangat perlahan.

Berbilang coroutine
  • versi, konkurensi, prestasi tinggi, tetapi terlalu banyak coroutine mungkin ranap.

  • Kolam coroutineversi, konkurensi, prestasi tinggi dan bilangan coroutine yang boleh dikawal.

Biasanya, jika asasnya mencukupi, lebih disyorkan guna kaedah coroutine pool.

Atas ialah kandungan terperinci Ajar anda langkah demi langkah cara membina pengimbas port TCP mudah menggunakan bahasa Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:Go语言进阶学习. Jika ada pelanggaran, sila hubungi admin@php.cn Padam