If the POSIX socket "read" function returns 0, does that indicate an error occurred?

Viewed 286

I'm working with legacy code. This code calls a "read" on a socket. If the read function returns a 0 or below, the calling function will throw an error.

n = read(sock, &buff[bytesRead], bytesToRead - bytesRead);
if (n <= 0) {
    syslog(LOG_CRIT, "ReadFromSocket: read() failed, errno=%d\n", errno);
    return FALSE;
}

However, I've noticed that this function is throwing an error with the "errno" = 0.

I'm curious if I'm running into an edge case where the read function is returning a 0, which is incorrectly being interpreted as an error.

Does a return value of 0 indicate an actual error?

4 Answers

No, a return value of zero does not indicate an error. The documentation for read() says:

Upon successful completion, these functions shall return a non-negative integer indicating the number of bytes actually read. Otherwise, the functions shall return -1 and set errno to indicate the error.

That is: A return value of zero just means that zero bytes have been actually read. This is not an error. An error is indicated by the return value -1. The fact that errno is still zero also is an indication that no error occurred.

As for the blocking / non-blocking part:

If fildes [i.e.: first parameter] refers to a socket, read() shall be equivalent to recv() with no flags set.

There it says:

If no messages are available at the socket and O_NONBLOCK is not set on the socket's file descriptor, recv() shall block until a message arrives. If no messages are available at the socket and O_NONBLOCK is set on the socket's file descriptor, recv() shall fail and set errno to [EAGAIN] or [EWOULDBLOCK].

Keeping in mind that the file is a socket, this translates to:

  • In blocking mode, the read()/recv() call will just wait until there is more data available. A return value of zero should therefore indicate that the socket has been closed and no more data will be recieved. One can argue whether this is an error or not, but it indicates an orderly shutdown, so I would not see it as error but just as "No more data to read here, move on!" instead.
  • In non-blocking mode, the read()/recv() call will return with -1 and errno could be either EAGAIN or EWOULDBLOCK. Zero can still occur as return value in non-blocking mode, if the socket has been closed properly.

Summary:

Zero does not indicate an error. Zero can be returned, if:

  • the socket has been closed in an orderly fashion and no more data can be received (in both blocking and non-blocking mode), or
  • a datagram of size zero has been received, or
  • exactly zero bytes have been requested via the third parameter to read().

According to the manual, read return -1 in case of error, or the number of byte read. So a 0 return value indicates no error, just that no bytes were read.

You code should be

n = read(sock, &buff[bytesRead], bytesToRead - bytesRead);
if (n < 0) {
    syslog(LOG_CRIT, "ReadFromSocket: read() failed, errno=%d\n", errno);
    return FALSE;
}

It depends whether the socket is in blocking or not blocking mode. In non blocking mode, a 0 reads just indicates that no data is currently available but more could come later. In blocking mode, a 0 read is an end of stream condition. It is not an error, but no data will ever be available after that point. It is generally caused by the peer calling shutdown or close.

read():

If fildes refers to a socket, read() shall be equivalent to recv() with no flags set.

recv():

If no messages are available at the socket and O_NONBLOCK is not set on the socket's file descriptor, recv() shall block until a message arrives. If no messages are available at the socket and O_NONBLOCK is set on the socket's file descriptor, recv() shall fail and set errno to [EAGAIN] or [EWOULDBLOCK].

Upon successful completion, recv() shall return the length of the message in bytes. If no messages are available to be received and the peer has performed an orderly shutdown, recv() shall return 0. Otherwise, -1 shall be returned and errno set to indicate the error.

The Linux man page for recv*() expands on that with:

These calls return the number of bytes received, or -1 if an error occurred. In the event of an error, errno is set to indicate the error.

When a stream socket peer has performed an orderly shutdown, the return value will be 0 (the traditional "end-of-file" return).

Datagram sockets in various domains (e.g., the UNIX and Internet domains) permit zero-length datagrams. When such a datagram is received, the return value is 0. > The value 0 may also be returned if the requested number of bytes to receive from a stream socket was 0.

So, no, a return value of zero does not indicate an error, but that

  • you asked for zero bytes and got it
  • the connection is closed, or
  • it's a datagram socket and you just read a zero-length datagram.
Related