Crismar
Crismar

Reputation: 63

How can I keep reading using net Conn Read method

I'm creating a simple chat server as a personal project to learn net package and some concurrency in go. My 1st idea is to make the server print whatever is send using nc command echo -n "hello" | nc -w1 -4 localhost 2016 -p 61865. However after the 1st read my code ignores the subsequent messages.

func (s *Server) messageReader(conn net.Conn) {
defer conn.Close()
buffer := make([]byte, 1024)
for {
    //read buff
    blen, err := conn.Read(buffer)
    if err != nil {
        log.Fatal(err)
    }

    message := string(buffer[:blen])

    if message == "/quit" {
        fmt.Println("quit command received. Bye.")
        return
    }

    if blen > 0 {
        fmt.Println(message)
        buffer = buffer[:0]
    }
}
}

// Run Start up the server. Manages join and leave chat
func (s *Server) Run() {
// Listen on port TCP 2016
listener, err := net.Listen("tcp", ":2016")
if err != nil {
    log.Fatal(err)
}
defer listener.Close()

for {
    //wait for connection
    conn, err := listener.Accept()
    if err != nil {
        log.Fatal(err)
    }

    go s.messageReader(conn)

}
}

If I send a new message from a new client it prints without problems but if I send another one it does nothing. What am I missing do I need to reset the Conn or close it and spawn a new one?

Upvotes: 0

Views: 5135

Answers (1)

JimB
JimB

Reputation: 109331

After printing your message, you slice buffer down to zero length. You can't read any data into a zero-length slice. There's no reason to re-slice your read buffer at all.

You also need to handle the read bytes before checking for errors, as io.EOF can be returned on a successful read.

You shouldn't use log.Fatal in the server's read loop, as that calls os.Exit

A working messageReader body might look like:

defer conn.Close()
buffer := make([]byte, 1024)
for {
    n, err := conn.Read(buffer)
    message := string(buffer[:n])

    if message == "/quit" {
        fmt.Println("quit command received. Bye.")
        return
    }

    if n > 0 {
        fmt.Println(message)
    }

    if err != nil {
        log.Println(err)
        return
    }
}

You should note though that because you're not using any sort of framing protocol here, you can't guarantee that each conn.Read returns a complete or single message. You need to have some sort of higher-level protocol to delimit messages in your stream.

Upvotes: 3

Related Questions