创建服务器并连接到它

时间:2018-09-22 15:26:53

标签: go networking server goroutine

我试图在Go中创建服务器并连接到它,但是当服务器开始运行时,由于启动服务器的功能似乎正在阻塞,因此未调用任何功能。我尝试使用goroutine在后台运行服务器,以便可以执行其他代码,但这也不起作用。我遇到了我不明白的错误

  

紧急:运行时错误:无效的内存地址或nil指针   解引用[signal SIGSEGV:细分违规代码= 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)
    }
}

我是做对了还是有其他方法来做我想做的事情?我有点不愿使用goroutine,因为在我的情况下引入异步行为可能是错误的。

编辑1 :由于客户端试图在服务器启动之前尝试连接服务器,因此似乎发生了此错误。

编辑2 :我(可能已经)通过在服务器中添加一个布尔通道来解决此问题,该布尔通道会在侦听器开始接受连接之前立即返回true。然后,仅当来自通道的值为true时,客户端才连接到服务器。在这种情况下,我不确定这是否是正确的解决方法,但它似乎可以正常工作。

1 个答案:

答案 0 :(得分:2)

您的服务器在goroutine中启动,该goroutine由Go运行时异步生成,并且需要一些时间来计划和执行。具体来说,运行时不会在继续执行main函数之前等待服务器goroutine启动。

您要么需要:

  • 安排服务器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。这是预料之中的,在某些情况下可以采用多种模式来避免使代码看起来过于重复。