这种恐慌的原因是什么?

What is the reason for this panic?

为了练习一些基本概念,我正在编写一个简单的端口扫描器。然而,当尝试实现 goroutines 时,程序崩溃并且我得到一个分段错误:

Scanning ports
{Port:139 State:Open}
{Port:135 State:Open}
{Port:136 State:Closed}
{Port:131 State:Closed}
{Port:131 State:Open}
{Port:134 State:Closed}
{Port:134 State:Open}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4eb26a]

goroutine 20 [running]:
main.scanPort(0x52033b, 0x3, 0x52203e, 0xf, 0x83)
        /home/athos/Projects/go-tutorial/scanner.go:33 +0x1ea
created by main.main
        /home/athos/Projects/go-tutorial/scanner.go:41 +0xf1
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4eb26a]

goroutine 23 [running]:
main.scanPort(0x52033b, 0x3, 0x52203e, 0xf, 0x86)
        /home/athos/Projects/go-tutorial/scanner.go:33 +0x1ea
created by main.main
        /home/athos/Projects/go-tutorial/scanner.go:41 +0xf1
exit status 2

这是我的代码:

package main

import (
    "fmt"
    "net"
    "strconv"
    "sync"
    "time"
)

var wg sync.WaitGroup

type scanResult struct {
    Port  int
    State string
}

func scanPort(protocol, hostname string, port int) {
    defer wg.Done()
    result := scanResult{Port: port}
    socket := hostname + ":" + strconv.Itoa(port)
    conn, err := net.DialTimeout(protocol, socket, 2*time.Second)

    if err != nil {
        result.State = "Closed"
        fmt.Printf("%+v\n", result)
    }

    result.State = "Open"
    fmt.Printf("%+v\n", result)

    // Defers: FILO data structure
    defer conn.Close()
}

func main() {
    fmt.Println("Scanning ports")
    for i := 130; i <= 145; i++ {
        wg.Add(1)
        go scanPort("tcp", "192.168.200.103", i)
    }
    // Wait for goroutines to complete
    wg.Wait()
}

谁能帮我看看我做错了什么?

net.DialTimeout() return是一个连接和一个错误,你正确检查错误是否不是nil,但即使有错误,你也只是打印它并继续。

如果出现非 nil 错误,您不应该(不得)使用 returned 连接,因为这可能是 nil 或无效值。如果有错误,请检查/打印它并 return,不要尝试使用 conn.

如此简单 return:

if err != nil {
    result.State = "Closed"
    fmt.Printf("%+v\n", result)
    return
}

此外,如果没有错误,您可以“安排”立即关闭连接,延迟。如果您在函数中最后关闭连接,则使用 defer 毫无意义。

所以它应该是这样的:

conn, err := net.DialTimeout(protocol, socket, 2*time.Second)

if err != nil {
    result.State = "Closed"
    fmt.Printf("%+v\n", result)
    return
}

defer conn.Close()

result.State = "Open"
fmt.Printf("%+v\n", result)