Blocking on Read().

3,560 views
Skip to first unread message

Bruno Albuquerque

unread,
May 12, 2013, 6:49:18 AM5/12/13
to golang-nuts
Hi.

I am trying to do something very simply, but I am failing miserably. I have this TCP server that listens to connections and, when a connection is received, it starts  goroutine to handle it. The goroutine is supposed to simply wait for data and process it when available.

My problem is that Read() seems to be non-blocking (when you try to read and there is no data available it returns EOF as an error. I tried using SetDeadline() to make the Read() call block but this did not work. This is a simplified version of the relevant code that shows the problem:

func handleTcpConn(conn net.Conn) {
        defer conn.Close()

        // This does not help with making Read() wait for data, as I expected it would do.
        conn.SetDeadline(time.Time{})

        // Send some data. This works as expected.
        conn.Write([]byte("Hi\n"))

        for {
               var buffer  []byte

                // Read data. This fails with EOF as the other side does not send data
                // immediatelly.
                _, err := conn.Read(buffer)
                if err != nil {
                        fmt.Println(err)
                        break
                 }
         }
}

Simply handling EOF and retrying is not a good solution, as this would result in a busy loop. I can add a Sleep(), but this is not really a solution. I am probably simply doing something pretty stupid.

Help?

-Bruno








roger peppe

unread,
May 12, 2013, 6:57:35 AM5/12/13
to Bruno Albuquerque, golang-nuts

You need to pass in some data for the Read to read into (make the buf, don't just declare it). I'm slightly surprised you get io.EOF even so.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Bruno Albuquerque

unread,
May 12, 2013, 8:28:29 AM5/12/13
to roger peppe, golang-nuts
That was it. Thanks.

The one time I should have thought in C terms, I did not. ;)


2013/5/12 roger peppe <rogp...@gmail.com>

Reck Hou

unread,
May 12, 2013, 8:02:06 PM5/12/13
to golan...@googlegroups.com
That's not the true reason why Read() returns EOF error. According to my experience, Read will return EOF error immediately without blocking when nothing was sent. Set deadline will only set timeout of closing TCP connection, I just check if the error was EOF, then continue, or break. If anyone knows how to make a blocking Read() like Unix system call, please reply.

Dave Cheney

unread,
May 12, 2013, 9:23:55 PM5/12/13
to Reck Hou, golan...@googlegroups.com
I'm sorry but that is wrong. Implementations of io.Reader block when you call read. From the point of view of your program Read() returns when something was read. Similarly, io.Reader implementations return io.EOF when no more can be read.



On 12/05/2013, at 17:02, Reck Hou <rec...@gmail.com> wrote:

> That's not the true reason why Read() returns EOF error. According to my experience, Read will return EOF error immediately without blocking when nothing was sent. Set deadline will only set timeout of closing TCP connection, I just check if the error was EOF, then continue, or break. If anyone knows how to make a blocking Read() like Unix system call, please reply.
>

Hou Shuaiying

unread,
May 13, 2013, 3:28:07 AM5/13/13
to Dave Cheney, golan...@googlegroups.com
Strangely, in my case Read() is non-blocking and caused high CPU usage.

My code:

In function main:

l, err := net.Listen("tcp", ":13798")
  if err != nil {
    log.Fatal(err)
  }
  
  for {
    // Wait for a connection.
    conn, err := l.Accept()
    if err != nil {
      log.Fatal(err)
    }
    // Handle the connection in a new goroutine.
    // The loop then returns to accepting, so that
    // multiple connections may be served concurrently.
    go reqHandler.TCPHandler(conn)

    runtime.Gosched()
  }

Function TCPHandler:

func TCPHandler(conn net.Conn) {
request := make([]byte, 4096)
  for {
    read_len, err := conn.Read(request)
    
    if err != nil {
        if err.Error() == "use of closed network connection" { // TODO: Hack way, this happens when high-level logic meets an error and closed connection.
        LOG("Conn closed, error might happened)
        break
      }

      neterr, ok := err.(net.Error);
      if ok && neterr.Timeout() {
        fmt.Println(neterr)
        LOG("Client timeout!")
        break
      }
    }

    if read_len == 0 {
     LOG("Nothing read")
      continue
    } else {
      // do something
    }
    request := make([]byte, 4096)
  }
}

The problem is, conn.Read is non-blocking, so every time it goes to "LOG("Nothing read")" then continue, so it's causing high CPU usage. How to make conn.Read a block call?

Best regards,
Shuaiying Hou





Tamás Gulácsi

unread,
May 13, 2013, 10:26:17 AM5/13/13
to golan...@googlegroups.com
Please delete : for the second make([]byte, 4096).

james4k

unread,
May 13, 2013, 2:29:17 PM5/13/13
to golan...@googlegroups.com, Dave Cheney
In your case, you seem to be ignoring all but a few specific errors.

Dave Cheney

unread,
May 13, 2013, 1:53:44 PM5/13/13
to Hou Shuaiying, golan...@googlegroups.com
You need to re read the contract for io.Reader, specifically the part where it say you should handle the data returned from a read call before consulting the error value. 


Reply all
Reply to author
Forward
0 new messages