在goroutines中扫描端口

在goroutines中扫描端口,go,goroutine,Go,Goroutine,我目前正在学习围棋。为此,我制作了一个相对简单的端口扫描程序 我面临的问题是扫描这些端口需要相当长的时间。我的行为是,如果我扫描端口(定义为int32数组(protobuf不支持int16),不使用goroutines是可行的,但是在扫描超过5个端口时会非常慢,正如您所想象的,因为它不是并行运行的 为了实现并行性,我提出了以下代码(解释+问题在代码之后): 如代码片段的第一行所示,我定义了一个切片来保存所有结果,其思想是我的应用程序并行扫描端口,完成后,将结果发送给感兴趣的人 但是,使用此代码时

我目前正在学习围棋。为此,我制作了一个相对简单的端口扫描程序

我面临的问题是扫描这些端口需要相当长的时间。我的行为是,如果我扫描端口(定义为int32数组(protobuf不支持int16),不使用goroutines是可行的,但是在扫描超过5个端口时会非常慢,正如您所想象的,因为它不是并行运行的

为了实现并行性,我提出了以下代码(解释+问题在代码之后):

如代码片段的第一行所示,我定义了一个切片来保存所有结果,其思想是我的应用程序并行扫描端口,完成后,将结果发送给感兴趣的人

但是,使用此代码时,程序会卡住

我运行此基准测试其性能:

func BenchmarkPortScan(b *testing.B) {
  request := &portscan.ScanPortsRequest{
      Ip:        "62.129.139.214",
      PortRange: "20,21,22,23",
      Timeout:   500,
  }

  svc := newService()

  for i := 0; i < b.N; i++ {
      svc.ScanPorts(nil, request)
  }
}
func BenchmarkPortScan(b*testing.b){
请求:=&portscan.ScanPortsRequest{
Ip:“62.129.139.214”,
端口范围:“20,21,22,23”,
超时:500,
}
svc:=newService()
对于i:=0;i
它被卡住的原因是什么?看这段代码有什么意义吗

简而言之,我希望我的最终结果是,每个端口在不同的go例程中被扫描,当它们全部完成时,所有的东西都会在扫描结果的一个结果片段中聚集在一起

我希望我已经说清楚了,并且提供了足够的信息给你们来帮助我


哦,我特别在寻找指针和学习点,而不是工作代码示例。

您需要在
wg.Wait()
之后关闭通道。否则,您的循环范围会被卡住


除此之外,您的代码看起来还不错。

您需要在
wg.Wait()
之后关闭频道。否则,您的循环范围会被卡住


除此之外,您的代码看起来很好。

正如@creker所写,您必须关闭通道,否则从中读取的循环将是无限循环。但是,我不同意仅在
wg.Wait()之后添加
close(ch)
是正确的方法-这意味着从通道读取值的循环在扫描所有端口之前不会启动(所有
connect()
调用返回)。我想说的是,您希望在结果可用时立即开始处理它们。为此,您必须重新构造代码,以便生产者和消费者是不同的goroutine,如下所示

var results []*portscan.ScanResult
ch := make(chan *portscan.ScanResult)

// launch the producer goroutine    
go func() {
   var wg sync.WaitGroup
   wg.Add(len(splitPorts))
   for _, port := range splitPorts {
       go func(port int32) {
          defer wg.Done()
          ch <- connect(ip, port, req.Timeout)
       }(port)
   }
   wg.Wait()
   close(ch)
}()

// consume results
for elem := range ch {
    results = append(results, elem)
}

func connect(ip string, port, timeout int32) *portscan.ScanResult {
    res := &portscan.ScanResult{
            Port:   port,
            IsOpen: false,
    }
    conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), time.Duration(timeout)*time.Millisecond)

    if err == nil {
        conn.Close()
        res.IsOpen = true
    }
    return res
}
var results[]*portscan.ScanResult
ch:=make(chan*portscan.scansult)
//推出制作人goroutine
go func(){
var wg sync.WaitGroup
工作组添加(len(拆分端口))
对于u,端口:=范围拆分端口{
go func(端口int32){
推迟工作组完成()

ch正如@creker所写,您必须关闭通道,否则从通道读取的循环将是无限循环。然而,我不同意仅在
wg.Wait()
之后添加
close(ch)
是正确的方法-这意味着从通道读取值的循环在扫描所有端口之前不会启动(所有
connect()
调用都返回)。我想说的是,您希望在结果可用时立即开始处理它们。为此,您必须重新构造代码,以便生产者和消费者是不同的goroutine,如下所示

var results []*portscan.ScanResult
ch := make(chan *portscan.ScanResult)

// launch the producer goroutine    
go func() {
   var wg sync.WaitGroup
   wg.Add(len(splitPorts))
   for _, port := range splitPorts {
       go func(port int32) {
          defer wg.Done()
          ch <- connect(ip, port, req.Timeout)
       }(port)
   }
   wg.Wait()
   close(ch)
}()

// consume results
for elem := range ch {
    results = append(results, elem)
}

func connect(ip string, port, timeout int32) *portscan.ScanResult {
    res := &portscan.ScanResult{
            Port:   port,
            IsOpen: false,
    }
    conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), time.Duration(timeout)*time.Millisecond)

    if err == nil {
        conn.Close()
        res.IsOpen = true
    }
    return res
}
var results[]*portscan.ScanResult
ch:=make(chan*portscan.scansult)
//推出制作人goroutine
go func(){
var wg sync.WaitGroup
工作组添加(len(拆分端口))
对于u,端口:=范围拆分端口{
go func(端口int32){
推迟工作组完成()

先生,你是英雄!我根本不知道你需要关闭频道。现在有意义了。谢谢你的解释。端口扫描的性能现在好多了。先生,你是英雄!我根本不知道你需要关闭频道。现在有意义了。谢谢你的解释。端口扫描的性能现在好多了r、 不要在闭包中直接使用循环变量
port
。当goroutine收到相同的值时,你会遇到错误。很好的捕获,更改了代码。整个过程都是在浏览器中编写的,没有编译,只是为了演示主要思想…@谢谢你的精彩回答。我已经接受了Creker的答案,因为它是确实回答并解决了我的问题,因此对此表示歉意。但对您的解决方案,我竖起大拇指。非常清楚,整体改进很好。不要在闭包中直接使用循环变量
port
。当goroutine收到相同的值时,您会遇到错误。很好,更改了代码。整个过程都是在浏览器中编写的,没有com堆砌它,只是为了展示主要思想…@谢谢你的精彩回答。我已经接受了Creker的回答,因为它确实回答并解决了我的问题,所以对此表示歉意。但我对你的解决方案竖起大拇指。非常清楚,整体上有很好的改进。
var results []*portscan.ScanResult
ch := make(chan *portscan.ScanResult)

// launch the producer goroutine    
go func() {
   var wg sync.WaitGroup
   wg.Add(len(splitPorts))
   for _, port := range splitPorts {
       go func(port int32) {
          defer wg.Done()
          ch <- connect(ip, port, req.Timeout)
       }(port)
   }
   wg.Wait()
   close(ch)
}()

// consume results
for elem := range ch {
    results = append(results, elem)
}

func connect(ip string, port, timeout int32) *portscan.ScanResult {
    res := &portscan.ScanResult{
            Port:   port,
            IsOpen: false,
    }
    conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), time.Duration(timeout)*time.Millisecond)

    if err == nil {
        conn.Close()
        res.IsOpen = true
    }
    return res
}