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