Home > Article > Backend Development > Teach you step by step how to build a simple TCP port scanner using Go language
When we use TCP to connect, we need to knowthe other party’s machineip:port
ConnectionIf successful, the process is as follows.
If there is normal, there will befailed, ifIf the connected party is closed, the process is as follows.
There is another possibility,The port is open, but is blocked by the firewall. The process is as follows.
##After you understand the essence, you can start coding.
In Go, we usually use net.Dial for TCP connection.
Justtwo situations
Success: Return conn.
Failed:err != nil
.
Relatively speaking, at the beginning, we may not be too bold. Write the prototype first, without considering performance.
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, "打开") } }
Execution Result
#But this process is very slow.
因为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) }
执行结果
In fact, 6W multiple threads are opened at the same time to scan each ip:port
.
So the time it takes the longest thread to end is the time the program ends.
It feels ok, I scanned more than 60,000 ports in 20 seconds! ! !
Above we are simple and crude The method creates a coroutine for each ip:port
.
Although in Go, theoretically it is no problem to open hundreds of thousands of coroutines, there is still some pressureof.
所以我们应该采用一种相对节约的方式进行精简代码,一般采用线程池方式。
本次使用的线程池包:gohive
地址:https://github.com/loveleshsharma/gohive
简单介绍
代码
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) }
执行结果
我设置的线程池大小是7w个,所以也是一下子开启6w多个协程的,但是我们已经可以进行线程大小约束了。
Suppose there is such a request now. There are 100 ip
, and each one needs to be scanned. ip
Open port, if you use a simple and crude way to open a thread.
That is100 65535=6552300
, more than 600 w threads still consume a lot of memory, and the system may crash if the thread pool method is used.
Control the thread pool to 500,000, maybe the situation will be much better.
But one thing is that in Go, the thread pool usually needs to be used in conjunction with chan
. It may be necessary Not a bad foundation.
This article is more of a fun article, let’s get to know it Fun stuff.
In fact, you can also connect through net.DialTimeout
ip:port
, this timeout can be set, for example, if it times out 5s, it will be determined that the port is not open.
I won’t give an example here.
We mainly use three methods to implement functions.
Normal version, no concurrency, very slow.
Multiple coroutines version, concurrency, high performance, but too many coroutines may crash.
Coroutine Pool version, concurrency, high performance, controllable number of coroutines.
Normally, if the foundation is OK, it is more RecommendedUse the coroutine pool Way.
The above is the detailed content of Teach you step by step how to build a simple TCP port scanner using Go language. For more information, please follow other related articles on the PHP Chinese website!