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.
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:
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"
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.