ホームページ  >  記事  >  バックエンド開発  >  Go 言語を使用して簡単な TCP ポート スキャナーを構築する方法を段階的に説明します。

Go 言語を使用して簡単な TCP ポート スキャナーを構築する方法を段階的に説明します。

Go语言进阶学习
Go语言进阶学习転載
2023-07-24 15:33:201626ブラウズ

#TCP スキャンの本質

#TCP を使用して接続する場合、相手のマシン#を知る必要があります。 ##ip:port通常のハンドシェイク

接続

成功した場合のプロセスは次のとおりです。

接続に失敗しました

正常であれば、failed となります。 , if接続先##がクローズされている場合は次のようになります。

Go 言語を使用して簡単な TCP ポート スキャナーを構築する方法を段階的に説明します。

#ファイアウォールがある場合


別の可能性もあります。

ポートは開いていますが、 はファイアウォールによってブロックされています。 処理は次のとおりです。 #コード

Go 言語を使用して簡単な TCP ポート スキャナーを構築する方法を段階的に説明します。

##本質を理解したら、コーディングを開始できます。


Go では通常、

TCP に

net.Dial を使用します。繋がり#########。

ちょうど2つの状況

  • 成功: 接続を返します。

  • #失敗しました:エラー != nil

通常版

比較的、最初はあまり大胆ではないかもしれません。最初にプロトタイプを書いてください。 、パフォーマンスを考慮せずに。

#コード

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

実行結果

Go 言語を使用して簡単な TCP ポート スキャナーを構築する方法を段階的に説明します。##ただし、このプロセスは非常に時間がかかります。

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

执行结果

Go 言語を使用して簡単な TCP ポート スキャナーを構築する方法を段階的に説明します。

実際には、各 ip:port をスキャンするために 6W の複数のスレッドが同時に開かれます。

したがって、#最長のスレッドが終了するまでにかかる時間 がプログラムの終了時間となります。

大丈夫です。20 秒間で 60,000 以上のポートをスキャンできました。 ! !

#スレッド プールのバージョン

上記は 単純かつ粗雑です このメソッドは、ip:port ごとにコルーチンを作成します。

Go では、理論的には数十万のコルーチンを開くことは問題ありませんが、それでもある程度の プレッシャーが存在します ## ##の。

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


本次使用的线程池包:gohive

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

简单介绍

Go 言語を使用して簡単な TCP ポート スキャナーを構築する方法を段階的に説明します。

代码

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

执行结果

Go 言語を使用して簡単な TCP ポート スキャナーを構築する方法を段階的に説明します。

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


このようなリクエストがあるとします。ip が 100 個あり、それぞれが必要とします。 ip単純かつ粗雑な方法でスレッドを開く場合は、ポートを開きます。

# #つまり、100 65535=6552300#、600 w スレッドを超えると依然として大量のメモリが消費され、スレッド プール方式が使用されるとシステムがクラッシュする可能性があります。 。

スレッド プールを 500,000 に制御すると、状況はさらに改善される可能性があります。

ただし、Go では通常、スレッド プールを chan と組み合わせて使用​​する必要があることが 1 つあります。 . 必要かもしれない 基礎としては悪くない。

概要

この記事は、楽しい記事です、詳しく見てみましょうそれ 楽しいこと

実際には、net.DialTimeout を通じて接続することもできます。 ip:port で、このタイムアウトを設定できます。たとえば、5秒タイムアウトした場合、ポートが開いていないと判断されます。

ここでは例を挙げません。

#主に 3 つのメソッド # を使用して関数を実装します。

  • 通常の バージョン、同時実行性がなく、非常に遅い。

  • 複数のコルーチン バージョン、同時実行性、高パフォーマンスですが、コルーチンが多すぎるとクラッシュする可能性があります。

  • コルーチン プール バージョン、同時実行性、高パフォーマンス、制御可能なコルーチンの数。

通常、基礎がOKであれば、さらに 推奨を使用します。コルーチンプールの方法。

以上がGo 言語を使用して簡単な TCP ポート スキャナーを構築する方法を段階的に説明します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はGo语言进阶学习で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。