aochagavia
aochagavia

Reputation: 6256

Two way connections in Go

I am trying to make a simple console chat in Go, just to practice. However, I don't know how to send messages back from the server. The server just receives a message and then closes the connection. How could I send a response?

I have been searching and found information about websockets, but I think that they are used to interact with browsers.

This are the two functions of the server:

func runServer() {
    // Listen on a port
    listen, error := net.Listen("tcp", ":8272")

    // Handles eventual errors
    if error != nil {
        fmt.Println(error)
        return
    }

    fmt.Println("Listening in port 8272.")

    for {
        // Accepts connections
        con, error := listen.Accept()

        // Handles eventual errors
        if error != nil {
            fmt.Println(error)
            continue
        }

        fmt.Println("Connection accepted.")

        // Handles the connection
        go handleConnection(con)
    }
}

func handleConnection(con net.Conn) {
    fmt.Println("Handling connection.")

    var message string

    // Decodes the received message
    decoder := gob.NewDecoder(con)
    error := decoder.Decode(&message)

    // Checks for errors
    if error != nil {
        fmt.Println(error)
    } else {
        fmt.Println("Received", message)
    }

    // Closes the connection
    con.Close()
    fmt.Println("Connection closed.")
}

This is the function of the client:

func runClient() {
    // Connects to server
    con, error := net.Dial("tcp", "127.0.0.1:8272")

    // Handles eventual errors
    if error != nil {
        fmt.Println(error)
        return
    }

    fmt.Println("Connected to 127.0.0.1:8272.")

    // Sends a message
    message := "Hello world"
    encoder := gob.NewEncoder(con)
    error = encoder.Encode(message)

    // Checks for errors
    if error != nil {
        fmt.Println(error)
    }

    con.Close()

    fmt.Println("Message sent. Connection closed.")
}

Thanks in advance.

Upvotes: 0

Views: 3568

Answers (1)

val
val

Reputation: 8689

Your con object is a connection, which has Read and Write methods described here: here. You should loop on the connection, trying to Read incoming data, then process it and (possibly) Write back the server response. (Here, bufferiopackage and the like can help you process it in a more convenient way, this is just the low-level ReadWriter interface)

The documentation shows a tiny example:

go func(c net.Conn) {
    // Echo all incoming data.
    io.Copy(c, c)
    // Shut down the connection.
    c.Close()
}(conn)

Which processes only the first message and then closes. You can process each incoming message like this:

go func(c net.Conn) {
    // Infinite loop: Get data, copy them and start over
    for {
        // Echo all incoming data.
        io.Copy(c, c)
    }
    // Shut down the connection.
    c.Close()
}(conn)

Replacing io.Copy with whatever is relevant to your server of course, so your example would be something like this:

func handleConnection(con net.Conn) {
    fmt.Println("Handling connection.")

    defer func() {
        // Closes the connection
        con.Close()
        fmt.Println("Connection closed.")
    }()

    var message string

    // Decodes the received message
    decoder := gob.NewDecoder(con)
    encoder := gob.NewEncoder(con)
    for {
        error := decoder.Decode(&message)

        // Checks for errors
        if error != nil {
            fmt.Println(error)
            // Exit the loop
            return
        } else {
            fmt.Println("Received", message)
            // Sending back
            if error = encoder.Encode(message); error != nil {
                 fmt.Println(error)
                 return
            } else {
                fmt.Println("Echo'd successfuly ! Waiting for next message...")
            }
        }
    }
}

Also, you should probably use package log instead of fmt for your logging messages (but this is irrelevant here).

A good place to understand how it all works is to browse the implementation of the default http server here.

Similarly, your client should loop using the same pattern:

LOOP:
    Send data (e.g. encoder.Encode)
    Receive data (e.g. decoder.Decode)
    if problem or termination -> break out of loop
END
close connection

Upvotes: 4

Related Questions