3

I have a connection, created like this:

conn, err = net.Dial("tcp", "127.0.0.1:20000")

I have tried reading from this connection in two ways. I think they both must work, but the first option doesn't.

Here is the first way of doing it:

var bytes []byte
for i := 0; i < 4; i++ {
    conn.Read(bytes)
}
fmt.Printf("%v", bytes)

The output of this method is:

[]

And here is the same thing, done with bufio.Reader:

func readResponse(conn net.Conn) (response string, err error) {
    reader := bufio.NewReader(conn)
    _, err = reader.Discard(8)
    if err != nil {
        return
    }

    response, err = reader.ReadString('\n')

    return
}

This function returns the response given by the server on the other end of the TCP connection.

Why does bufio.Reader.Read() work, but net.Conn.Read() doesn't?

icza
  • 389,944
  • 63
  • 907
  • 827
Parham Doustdar
  • 2,019
  • 3
  • 22
  • 41
  • 1
    Well, that is dead obvious: `Read` reads **into** the provided `[]byte`. Your `[]byte` is empty, it has zero length and this whole zero length is filled. So technically your `Read`call worked as it completely filled the provided byte slice with data from the connection. You took the Tour of Go? – Volker Jan 11 '16 at 08:54
  • Thanks. I am new to a lot of the concepts, so some things I read in the tour or the first time I look at the documentation of a function goes over my head without me realizing it. Sorry for the irritation. – Parham Doustdar Jan 11 '16 at 09:00

1 Answers1

12

The Conn.Read() method is to implement io.Reader, the general interface to read data from any source of bytes into a []byte. Quoting from the doc of Reader.Read():

Read reads up to len(p) bytes into p.

So Read() reads up to len(p) bytes but since you pass a nil slice, it won't read anything (length of a nil slice is 0). Please read the linked doc to know how Reader.Read() works.

Reader.Read() does not allocate a buffer ([]byte) where the read data will be stored, you have to create one and pass it, e.g.:

var buf = make([]byte, 100)
n, err := conn.Read(buf)
// n is the number of read bytes; don't forget to check err!

Don't forget to always check the returned error which may be io.EOF if end of data is reached. The general contract of io.Reader.Read() also allows returning some non-nil error (including io.EOF) and some read data (n > 0) at the same time. The number of read bytes will be in n, which means only the first n bytes of the buf is useful (in other words: buf[:n]).

Your other example using bufio.Reader works because you called Reader.ReadString() which doesn't require a []byte argument. If you would've used the bufio.Reader.Read() method, you would also had to pass a non-nil slice in order to actually get some data.

icza
  • 389,944
  • 63
  • 907
  • 827
  • 1
    I'd also note that novices often get tripped by a false expectation of `Read()` reading exactly so much data to fill the whole slice. The "problem" is in those "up to" words in the documentation string which actually stand to mean "any number of bytes between 1 and the length of the input buffer". To fill the whole buffer, one should use [`io.ReadFull()`](https://golang.org/pkg/io/#ReadFull). This behaviour follows the semantics of the [`read()` syscall of POSIX](http://pubs.opengroup.org/onlinepubs/009695399/functions/read.html). – kostix Jan 11 '16 at 19:17