0
votes

I'm trying to create a server and connect to it in Go but when the server starts running, no functions after it are called because the function that starts the server appears to be blocking. I tried using a goroutine to run the server in the background so that other code can be executed but that's not really working either. I'm getting an error that I don't understand

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

Here is my code for the server

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"))
    }
}

And here is the code for the client.

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)
    }
}

Am I doing this right or is there another approach to do what I'm trying to do? I'm a little hesistant to use goroutines because introducing async behavior might be wrong in my case.

EDIT 1: looks like this error is happening because the client is trying to connect to the server before it starts.

EDIT 2: I (might have) fixed this by adding a boolean channel in the server that returns true right before the listeners starts accepting connections. Then the client only connects to the server if the value coming from the channel is true. I'm not sure if this is the correct fix in this scenario but it appears to be working.

1
Do not ignore error return values... ever.RayfenWindspear
Had you been checking errors, you would have received this dial tcp 127.0.0.1:5000: getsockopt: connection refused which would have immediately told you what the problem was.RayfenWindspear

1 Answers

2
votes

Your server is started in a goroutine, which is spawned asynchronously by the Go runtime and will take some time to be scheduled and executed. Specifically, the runtime will not wait for the server goroutine to be started before continuing execution of the main function.

You either need to:

  • arrange for the server goroutine to communicate when the server is ready to accept connections – use of a channel is an acceptable mechanism for this; or
  • have your client connection attempt to connect repeatedly until the connection is accepted – in effect polling the server's readiness state. For example:

    // 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 values

As noted in a comment to your question, your code ignores error values in several places.

Please don't do this in real production code. Errors are returned for a reason, and may indicate a problem external to your code which prevents your program from properly continuing execution. You need to check for error values and take appropriate action where they are returned. The other value(s) returned from a function call, especially anything in the standard library, are unlikely to be meaningful if the returned error is not nil.

Yes, you end up writing if err != nil a lot. That's expected, and there are various patterns you can employ in some cases to avoid making your code seem excessively repetitive.