我正在尝试使用stdlib制作某种端口扫描程序。这比其他任何事情都更重要,因此请不要对涉及的逻辑发表评论。
看下面的代码:
package main
import (
"flag"
"fmt"
"net"
"time"
"strings"
"strconv"
"log"
"sync"
)
var commonPorts = map[int]string {
21: "ftp",
22: "sftp",
80: "http",
110: "pop3",
143: "imap",
443: "https",
631: "ipp",
993: "imaps",
995: "pop3s",
}
type OP struct {
mu sync.Mutex
ports []string
}
func (o *OP) SafeAdd(port string) {
o.mu.Lock()
defer o.mu.Unlock()
o.ports = append(o.ports, port)
}
func worker(host string, port int) string {
address := fmt.Sprintf("%s:%d", host, port)
conn, err := net.DialTimeout("tcp", address, time.Second * 3)
if err != nil {
return ""; // is offline, cannot connect
}
conn.Close()
stringI := strconv.Itoa(port)
if name, ok := commonPorts[port]; ok {
stringI += fmt.Sprintf("(%s)", name)
}
return stringI
}
func processWithChannels(host string) <-chan string{
openPort := make(chan string, 1000)
var wg sync.WaitGroup
for i := 1; i <= 65535; i++ {
wg.Add(1)
go func(openPort chan string, host string, i int) {
defer wg.Done()
port := worker(host, i)
if port != "" {
openPort <- port
}
}(openPort, host, i)
}
wg.Wait()
close(openPort)
return openPort
}
func main() {
var host = flag.String("host", "127.0.0.1", "please insert the host")
var pType = flag.Int("type", 2, "please insert the type")
flag.Parse()
fmt.Printf("Scanning: %s...\n", *host)
if _, err := net.LookupHost(*host); err != nil {
log.Fatal(err)
}
openPorts := &OP{ports: []string{}};
if *pType == 1 {
ports := processWithChannels(*host);
for port := range ports {
openPorts.SafeAdd(port)
}
} else {
var wg sync.WaitGroup
for i := 1; i <= 65535; i++ {
wg.Add(1)
go func(o *OP, host string, i int){
defer wg.Done()
if port := worker(host, i); port != "" {
o.SafeAdd(port)
}
}(openPorts, *host, i)
}
wg.Wait()
}
if len(openPorts.ports) > 0 {
fmt.Printf("Following ports are opened: %s\n", strings.Join(openPorts.ports, ", "))
} else {
fmt.Printf("No open port on the host: %s!\n", *host)
}
}
有两种开始扫描的方法,一种是使用缓冲通道,另一种是使用sync.GroupWait,一旦完成所有扫描,便可以纾困。
在我看来,在这种情况下,使用sync.GroupWait比使用缓冲的通道并循环遍历直到它为空更有意义。但是,在这里使用缓冲通道,除了使用另一个sync.WaitGroup块,我看不到一种方法可以检测到通道上没有其他东西,并且我应该从for循环中解脱出来。
我想我的问题是,如果我只想使用缓冲通道解决方案,我如何正确实现它,以便我知道何时完成处理,以便可以继续处理其余代码? (请不要建议超时)。
如果有人感兴趣的话,这也是这两种类型的小型基准测试:
MacBook-Pro:PortScanner c$ time ./PortScanner -host yahoo.com -type 1
Scanning: yahoo.com...
Following ports are opened: 80(http), 143(imap), 110(pop3), 995(pop3s), 993(imaps)
real 0m4.620s
user 0m1.193s
sys 0m1.284s
MacBook-Pro:PortScanner c$ time ./PortScanner -host yahoo.com -type 2
Scanning: yahoo.com...
Following ports are opened: 110(pop3), 80(http), 143(imap), 995(pop3s), 993(imaps)
real 0m4.055s
user 0m1.051s
sys 0m0.946s
最佳答案
如果您需要在通道中放入1000多个项目,对processWithChannels
的调用将挂起。如果要使用缓冲通道保存所有值直到处理,则必须有足够的容量来接受所有值。
如果要将所有值收集到一个片中,则没有理由使用通道,第二种解决方案就可以了。
如果要尽快“流式”传输端口,则需要在两种解决方案之间进行操作
ports := make(chan string)
var wg sync.WaitGroup
for i := 1; i <= 65535; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
if port := worker(*host, i); port != "" {
ports <- port
}
}(i)
}
go func() {
wg.Wait()
close(ports)
}()
for port := range ports {
fmt.Println("PORT:", port)
}
但是,这可能会出现问题,例如当您同时拨打所有65535端口时缺少打开的端口。这是使用工作池并发拨号的一种可能模式:
ports := make(chan string)
toScan := make(chan int)
var wg sync.WaitGroup
// make 100 workers for dialing
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for p := range toScan {
ports <- worker(*host, p)
}
}()
}
// close our receiving ports channel once all workers are done
go func() {
wg.Wait()
close(ports)
}()
// feed the ports to the worker pool
go func() {
for i := 1; i <= 65535; i++ {
toScan <- i
}
// signal the workers to stop
close(toScan)
}()
for port := range ports {
if port != "" {
fmt.Println("PORT:", port)
}
}
关于go - 在这种情况下如何正确循环通过缓冲 channel ?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31572753/