创建服务器并连接到它

Create server and connect to it

我正在尝试创建一个服务器并在 Go 中连接到它,但是当服务器启动时 运行ning,调用后没有任何函数,因为启动服务器的函数似乎被阻塞了。我尝试在后台使用 goroutine 运行 服务器,以便可以执行其他代码,但这也没有真正起作用。我收到一个我不明白的错误

panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x496757]

这是我的服务器代码

func RunServer(port string) {

    fmt.Println("Launching server...")
    ln, err := net.Listen("tcp", port)
    conn, _ := ln.Accept()

    // run loop forever (or until ctrl-c)
    for {

        // will listen for message to process ending in newline (\n)
        message, _ := bufio.NewReader(conn).ReadString('\n')

        // output message received
        fmt.Print("Message Received:", string(message))

        // sample process for string received
        newmessage := strings.ToUpper(message)

        // send new string back to client
        conn.Write([]byte(newmessage + "\n"))
    }
}

这是客户端的代码。

func createInitalClient(port int) {

    // run server then create a client that connects to it
    portToRunOn := fmt.Sprintf("%s%d", ":", port)
    go RunServer(portToRunOn)
}

func main() {

    createInitalClient(5000)

    // connect to this socket
    conn, _ := net.Dial("tcp", "127.0.0.1:5000")

    for {
        // read in input from stdin

        reader := bufio.NewReader(os.Stdin)
        fmt.Print("Text to send: ")
        text, _ := reader.ReadString('\n')

        // send to socket
        fmt.Fprintf(conn, text+"\n")

        // listen for reply
        message, _ := bufio.NewReader(conn).ReadString('\n')
        fmt.Print("Message from server: " + message)
    }
}

我这样做是否正确,或者是否有其他方法可以完成我想做的事情?我对使用 goroutines 有点犹豫,因为在我的情况下引入异步行为可能是错误的。

编辑 1:看起来发生此错误是因为客户端在启动前尝试连接到服务器。

编辑 2:我(可能已经)通过在服务器中添加一个布尔通道来修复此问题,该通道在侦听器开始接受连接之前 returns 为真。然后,如果来自通道的值为真,则客户端仅连接到服务器。我不确定在这种情况下这是否是正确的修复,但它似乎有效。

您的服务器在 goroutine 中启动,该 goroutine 由 Go 运行时异步生成,需要一些时间来安排和执行。具体来说,运行时不会等待服务端 goroutine 启动,然后继续执行 main 函数。

您需要:

  • 安排服务器 goroutine 在服务器准备好接受连接时进行通信——使用通道是一种可接受的机制;或者
  • 让您的客户端连接尝试重复连接,直到连接被接受——实际上轮询服务器的就绪状态。例如:

    // Predeclare "conn" to ensure it remains in scope outside the loop
    var conn net.Conn
    
    for {
        var err error
        conn, err = net.Dial("tcp", "127.0.0.1:5000")
    
        // Failed connection, try again in a second.
        // TODO: limit the number of attempts before giving up.
        if err != nil {
            time.Sleep(time.Second)
            continue
        }
    
        // Connected successfully. Continue.
        break
    }
    
    // do something with "conn"
    

错误值

如对您的问题的评论中所述,您的代码忽略了多个位置的错误值。

请不要在实际生产代码中这样做。返回错误是有原因的,并且可能表明代码外部存在问题,导致程序无法正常继续执行。您需要检查错误值并在错误值返回时采取适当的措施。如果返回的 error 不是 nil.

,则从函数调用返回的其他值(尤其是标准库中的任何值)不太可能有意义

是的,您最终 if err != nil 写了很多。这是预料之中的,在某些情况下您可以使用各种模式来避免让您的代码看起来过于重复。