自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

手把手教你用Go語言打造一款簡易TCP端口掃描器

開發(fā) 前端
這次呢, 咱們來實現(xiàn)一個簡單的TCP端口掃描器!

[[374779]]

 前言

Hey,大家好呀,我是碼農(nóng),星期八。

這次呢, 咱們來實現(xiàn)一個簡單的TCP端口掃描器!

也來體驗一下黑客的風采!

TCP掃描本質(zhì)

我們在使用TCP進行連接時,需要知道對方機器的ip:port

正常握手

連接成功的話,流程如下。


連接失敗

有正常,就有失敗,如果被連接方關(guān)閉的話,流程如下。


如果有防火墻

還有一種可能是,端口開放,但是防火墻攔截,流程如下。


代碼

本質(zhì)理解之后,就可以開始擼代碼了。

在Go中,我們通常使用net.Dial進行TCP連接。

它就兩種情況

  • 成功:返回conn。
  • 失敗:err != nil。

普通版

相對來說,剛開始時,我們可能都不是太膽大,都是先寫原型,也不考慮性能。

代碼

  1. package main 
  2.  
  3. import ( 
  4.     "fmt" 
  5.     "net" 
  6.  
  7. func main() { 
  8.     var ip = "192.168.43.34" 
  9.     for i := 21; i <= 120; i++ { 
  10.         var address = fmt.Sprintf("%s:%d", ip, i) 
  11.         conn, err := net.Dial("tcp", address) 
  12.         if err != nil { 
  13.             fmt.Println(address, "是關(guān)閉的"
  14.             continue 
  15.         } 
  16.         conn.Close() 
  17.         fmt.Println(address, "打開"
  18.   } 

執(zhí)行結(jié)果


但是這個過程是非常緩慢的。

因為net.Dial如果連接的是未開放的端口,一個端口可能就是20s+,所以,我們?yōu)槭裁磳W習多線程懂了把!!!

多線程版

上述是通過循環(huán)去一個個連接ip:port的,那我們就知道了,在一個個連接的位置,讓多個人去干就好了。

所以,多線程如下。

代碼

  1. package main 
  2.  
  3. import ( 
  4.     "fmt" 
  5.     "net" 
  6.     "sync" 
  7.     "time" 
  8.  
  9. func main() { 
  10.  
  11.     var begin =time.Now() 
  12.     //wg 
  13.     var wg sync.WaitGroup 
  14.     //ip 
  15.     var ip = "192.168.99.112" 
  16.     //var ip = "192.168.43.34" 
  17.     //循環(huán) 
  18.     for j := 21; j <= 65535; j++ { 
  19.         //添加wg 
  20.         wg.Add(1) 
  21.         go func(i int) { 
  22.             //釋放wg 
  23.             defer wg.Done() 
  24.             var address = fmt.Sprintf("%s:%d", ip, i) 
  25.             //conn, err := net.DialTimeout("tcp", address, time.Second*10) 
  26.             conn, err := net.Dial("tcp", address) 
  27.             if err != nil { 
  28.                 //fmt.Println(address, "是關(guān)閉的", err) 
  29.                 return 
  30.             } 
  31.             conn.Close() 
  32.             fmt.Println(address, "打開"
  33.         }(j) 
  34.     //等待wg 
  35.     wg.Wait() 
  36.     var elapseTime = time.Now().Sub(begin
  37.     fmt.Println("耗時:", elapseTime) 

執(zhí)行結(jié)果

 

其實是同時開啟了6W多個線程,去掃描每個ip:port。

所以耗時最長的線程結(jié)束的時間,就是程序結(jié)束的時間。

感覺還行,20s+掃描完6w多個端口!!!

線程池版

上面我們簡單粗暴的方式為每個ip:port都創(chuàng)建了一個協(xié)程。

雖然在Go中,理論上協(xié)程開個幾十萬個都沒問題,但是還是有一些壓力的。

所以我們應該采用一種相對節(jié)約的方式進行精簡代碼,一般采用線程池方式。

本次使用的線程池包:gohive

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

簡單介紹


代碼

  1. package main 
  2.  
  3. //線程池方式 
  4. import ( 
  5.     "fmt" 
  6.     "github.com/loveleshsharma/gohive" 
  7.     "net" 
  8.     "sync" 
  9.     "time" 
  10.  
  11. //wg 
  12. var wg sync.WaitGroup 
  13.  
  14. //地址管道,100容量 
  15. var addressChan = make(chan string, 100) 
  16.  
  17. //工人 
  18. func worker() { 
  19.     //函數(shù)結(jié)束釋放連接 
  20.     defer wg.Done() 
  21.     for { 
  22.         address, ok := <-addressChan 
  23.         if !ok { 
  24.             break 
  25.         } 
  26.         //fmt.Println("address:", address) 
  27.         conn, err := net.Dial("tcp", address) 
  28.         //conn, err := net.DialTimeout("tcp", address, 10) 
  29.         if err != nil { 
  30.             //fmt.Println("close:", address, err) 
  31.             continue 
  32.         } 
  33.         conn.Close() 
  34.         fmt.Println("open:", address) 
  35. func main() { 
  36.     var begin = time.Now() 
  37.     //ip 
  38.     var ip = "192.168.99.112" 
  39.     //線程池大小 
  40.     var pool_size = 70000 
  41.     var pool = gohive.NewFixedSizePool(pool_size) 
  42.  
  43.     //拼接ip:端口 
  44.     //啟動一個線程,用于生成ip:port,并且存放到地址管道種 
  45.     go func() { 
  46.         for port := 1; port <= 65535; port++ { 
  47.             var address = fmt.Sprintf("%s:%d", ip, port) 
  48.             //將address添加到地址管道 
  49.             //fmt.Println("<-:",address) 
  50.             addressChan <- address 
  51.         } 
  52.         //發(fā)送完關(guān)閉 addressChan 管道 
  53.         close(addressChan) 
  54. }() 
  55.     //啟動pool_size工人,處理addressChan種的每個地址 
  56.     for work := 0; work < pool_size; work++ { 
  57.         wg.Add(1) 
  58.         pool.Submit(worker) 
  59.     //等待結(jié)束 
  60.     wg.Wait() 
  61.     //計算時間 
  62.     var elapseTime = time.Now().Sub(begin
  63.     fmt.Println("耗時:", elapseTime) 

執(zhí)行結(jié)果


我設置的線程池大小是7w個,所以也是一下子開啟6w多個協(xié)程的,但是我們已經(jīng)可以進行線程大小約束了。

假設現(xiàn)在有這樣的去求,有100個ip,需要掃描每個ip開放的端口,如果采用簡單粗暴開線程的方式.

那就是100+65535=6552300,600多w個線程,還是比較消耗內(nèi)存的,可能系統(tǒng)就會崩潰,如果采用線程池方式。

將線程池控制在50w個,或許情況就會好很多。

但是有一點的是,在Go中,線程池通常需要配合chan使用,可能需要不錯的基礎。

總結(jié)

本篇更偏向于樂趣篇,了解一下好玩的玩意。

其實還可以通過net.DialTimeout連接ip:port,這個可以設置超時時間,比如超時5s就判定端口未開放。

此處就不做舉例了。

咱們主要使用三種方式來實現(xiàn)功能。

  • 正常版,沒有并發(fā),速度很慢。
  • 多協(xié)程版,并發(fā),性能很高,但是協(xié)程太多可能會崩潰。
  • 協(xié)程池版,并發(fā),性能高,協(xié)程數(shù)量可控。

通常情況下,如果基礎可以,更推薦使用協(xié)程池方式。

用微笑告訴別人,今天的我比昨天強,今后也一樣。

 

責任編輯:姜華 來源: Go語言進階學習
相關(guān)推薦

2024-01-07 20:00:27

2021-01-21 06:04:55

Go語言TCP目錄生成器

2023-05-22 10:04:24

2021-02-01 08:41:06

Java考試系統(tǒng)

2017-09-14 09:09:04

php應用LibreOfficeWord轉(zhuǎn)HTML

2021-02-04 15:52:46

Java考試系統(tǒng)

2021-01-04 09:55:26

Java移動互聯(lián)網(wǎng)

2021-01-05 09:04:20

Javatxt文件

2022-02-17 10:26:17

JavaScript掃雷游戲前端

2021-12-30 08:56:57

Python摸魚倒計界面Python基礎

2021-11-01 10:26:07

CanvasAPI畫布技術(shù)HTML5

2021-08-13 09:01:31

Python小游戲Python基礎

2021-08-09 13:31:25

PythonExcel代碼

2022-01-24 11:02:27

PySimpleGUPython計算器

2022-10-19 14:30:59

2011-03-28 16:14:38

jQuery

2021-02-04 09:00:57

SQLDjango原生

2021-02-06 14:55:05

大數(shù)據(jù)pandas數(shù)據(jù)分析

2022-06-30 16:10:26

Python計時器裝飾器

2022-08-04 10:39:23

Jenkins集成CD
點贊
收藏

51CTO技術(shù)棧公眾號