0

I'm trying to write a simple sockets based go server. I'm just wondering how does the connection.Read below knows when to stop reading. (Note: this is not my code, I copied it from Unix Sockets in Go as example)

package main

import (
  "log"
  "net"
)

func echoServer(c net.Conn) {
  for {
    buf := make([]byte, 512)
    nr, err := c.Read(buf)
    if err != nil {
        return
    }

    data := buf[0:nr]
    println("Server got:", string(data))
    _, err = c.Write(data)
    if err != nil {
        log.Fatal("Write: ", err)
    }
  }
}

func main() {
  l, err := net.Listen("unix", "/tmp/echo.sock")
  if err != nil {
    log.Fatal("listen error:", err)
  }

  for {
    fd, err := l.Accept()
    if err != nil {
        log.Fatal("accept error:", err)
    }

    go echoServer(fd)
  }
 }

Is it the EOF character or there's something else? It would be really helpful if someone can point me to a link official go docs. Thanks.

mlemboy
  • 95
  • 2
  • 6
  • EOF seems likely. There is an error returned on `c.Read(buf)`. Find the implementation of that to get your answer, but the error returned from there is the only thing that will make it stop reading. – Lansana Camara Aug 02 '18 at 12:27
  • 2
    There's no such thing as an `EOF` character. The Read function returns an `EOF` error value at the end of the stream. Also that example isn't very good, It doesn't read correctly (you need to check the bytes read before the error), and needlessly creates a new buffer every iteration of the loop. – JimB Aug 02 '18 at 13:14
  • @JimB I found your comment is useful. But, could you please elaborate more on what you mean by "check the bytes read before the error" and the reason behind it? – Jun Hsieh Jan 04 '21 at 17:56
  • 1
    @JunXie, because that is what the documentation states for an [`io.Reader`](https://pkg.go.dev/io/#Reader): "Callers should always process the n > 0 bytes returned before considering the error" – JimB Jan 04 '21 at 18:01

2 Answers2

3

This is the implementation of the default Read method on net.Conn.Read:

// Read implements the Conn Read method.
func (c *conn) Read(b []byte) (int, error) {
    if !c.ok() {
        return 0, syscall.EINVAL
    }
    n, err := c.fd.Read(b)
    if err != nil && err != io.EOF {
        err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
    }
    return n, err
}

This is the implementation of the c.fd.Read(b) that is called within the function above:

func (fd *netFD) Read(p []byte) (n int, err error) {
    if err := fd.readLock(); err != nil {
        return 0, err
    }
    defer fd.readUnlock()
    if len(p) == 0 {
        // If the caller wanted a zero byte read, return immediately
        // without trying. (But after acquiring the readLock.) Otherwise
        // syscall.Read returns 0, nil and eofError turns that into
        // io.EOF.
        // TODO(bradfitz): make it wait for readability? (Issue 15735)
        return 0, nil
    }
    if err := fd.pd.prepareRead(); err != nil {
        return 0, err
    }
    if fd.isStream && len(p) > 1<<30 {
        p = p[:1<<30]
    }
    for {
        n, err = syscall.Read(fd.sysfd, p)
        if err != nil {
            n = 0
            if err == syscall.EAGAIN {
                if err = fd.pd.waitRead(); err == nil {
                    continue
                }
            }
        }
        err = fd.eofError(n, err)
        break
    }
    if _, ok := err.(syscall.Errno); ok {
        err = os.NewSyscallError("read", err)
    }
    return
}

So, yes, an EOF will make it stop reading. But so will plenty of other non-nil errors.

Lansana Camara
  • 9,497
  • 11
  • 47
  • 87
1

It will stop reading when its underlying implementation hits any error. Error may be an actual I/O error, or it could be the operating system signaling connection closed with io.EOF, or it could be a timeout, so on.

Everton
  • 12,589
  • 9
  • 47
  • 59