net package: Read & EOF & Timeout

2,905 views
Skip to first unread message

HaWe

unread,
Sep 13, 2010, 9:30:25 AM9/13/10
to golang-nuts
Hacking my first client/server application I had difficulties
recognizing the end of a message in a Read-loop.

The client does well because it gets a os.EOF with its last Read.

The server gets no EOF: the last Read is blocking. So I use the
timeout mechanism which seems a bit clumsy.

So here are my questions:
1) Why does Read behave differently for client and server? (Or is it
the Write from the other side?)
2) Is there a better way reading messages of arbitrary length?

Thanks in advance.

HaWe


And here comes the (stripped down) code.

client.go:

package main

import (
"net"
"fmt"
"os"
)

func main() {
conn, _ := net.Dial("tcp", "", ":3333")
defer conn.Close()

var req string = "1234567890"
conn.Write([]byte(req))

var resp string
var buf [5]byte
for {
n, err := conn.Read(buf[0:])
if n > 0 {
fmt.Println("client: Read", string(buf[0:n]))
resp += string(buf[0:n])
}
if err == os.EOF { // (1) works here
fmt.Println("client: Read EOF")
break
}
if err != nil {
fmt.Println("client: Read", err)
return
}
}
}

server.go:

package main

import (
"net"
"os"
"fmt"
)

func main() {
horch, _ := net.Listen("tcp", ":3333")
for {
client, _ := horch.Accept()
go func() {
defer client.Close()
var req string
var buf [5]byte
client.SetReadTimeout(1e8) // 100 ms
for {
n, err := client.Read(buf[0:])
if n > 0 {
fmt.Println("server: Read", string(buf[0:n]))
req += string(buf[0:n])
}
if err == os.EOF { // (1) no EOF
delivered here
fmt.Println("server: Read EOF")
break
}
if err != nil {
if neterr, ok := err.(net.Error); ok && neterr.Timeout() { //
timeout instead of EOF
fmt.Println("server: Read timeout")
break
}
client.Write([]byte("server: "+err.String()))
return
}
}
client.Write([]byte(req))
}()
}
}

Russ Cox

unread,
Sep 13, 2010, 10:11:25 AM9/13/10
to HaWe, golang-nuts

Client or server shouldn't matter.
When one side hangs up the other sees EOF.
In your program the client never hangs up,
so the server never sees EOF.

Russ

fango

unread,
Sep 13, 2010, 10:41:29 AM9/13/10
to golang-nuts
tcp has no message boundary. It is just a stream. Try impose your own
message struct upon it, eg, JSON

Cheers,
Fango

HaWe

unread,
Sep 13, 2010, 10:58:06 AM9/13/10
to golang-nuts
The server hangs up with the Close? Ok, and the client can't do that
directly after its Write; it needs the response first. So, there is no
remedy here.

@fango
Yes, sure, it will be JSON. But how can I decide when it's all there
and I can call Unmarshal. Timeout was the only idea yet.

HaWe

André Moraes

unread,
Sep 13, 2010, 11:02:28 AM9/13/10
to golang-nuts
You can use two distinct algorithms to handle end of messages

1: The first bytes of the message (maybe an int32) mark the size of the message, so the server knows when the message ends. This approach requires the client to know "a priore" the size of the message befor starting to send it.
2: The first bytes of the message is a mark (maybe an GUID) and after all the bytes are trasnfered your client sends the mark, so when the server sees the mark it knows that the message has ended. This approach requires you to make an escape scheme so when the client tries to write the mark in the middle of the message it will escape it with some byte sequence (more or less like you do when you want to print "\n", you write "\\n").

But, if both client and server are Go programs, you can use NetChannel which handle everything for you.

HaWe

unread,
Sep 13, 2010, 11:13:24 AM9/13/10
to golang-nuts
Yes, in the beginning I thought about sending the size first which
then appeared a bit too low level to me.

And thanks, I'll look into NetChannel. (You meant the netchan
package?)

HaWe

Russ Cox

unread,
Sep 13, 2010, 11:17:02 AM9/13/10
to HaWe, golang-nuts
> @fango
> Yes, sure, it will be JSON. But how can I decide when it's all there
> and I can call Unmarshal.

If you're using JSON, use json.NewEncoder
and json.NewDecoder. They'll take care of
message boundaries for you and make it possible
to just read and write JSON-able objects on the
connection.

Russ

HaWe

unread,
Sep 13, 2010, 11:24:34 AM9/13/10
to golang-nuts
Sounds great.
HaWe

HaWe

unread,
Sep 13, 2010, 2:06:53 PM9/13/10
to golang-nuts
And it is.
Reply all
Reply to author
Forward
0 new messages