Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Case when blocking recv() returns less than requested bytes

The recv() library function man page mention that:

It returns the number of bytes received. It normally returns any data available, up to the requested amount, rather than waiting for receipt of the full amount requested.

If we are using blocking recv() call and requested for 100 bytes:

recv(sockDesc, buffer, size, 0); /* Where size is 100. */

and only 50 bytes are send by the server then this recv() is blocked until 100 bytes are available or it will return receiving 50 bytes.

The scenario could be that:

  • server crashes after sendign only 50 bytes

  • bad protocol design where server is only sending 50 bytes while client is expecting 100 and server is also waiting for client's reply (i.e. socket close connection has not been initiated by server in which recv will return)

I am interested on Linux / Solaris platform. I don't have the development environment to check it out myself.

like image 514
Adil Avatar asked Feb 19 '10 10:02

Adil


People also ask

Is recv () a blocking call?

recv(IPC, Buffer, int n) is a blocking call, that is, if data is available it writes it to the buffer and immediately returns true, and if no data is available it waits for at least n seconds to receive any data.

Under which of the following circumstances recv () returns a zero value?

A returned value of zero indicates one of the following: The partner program has sent a NULL message (a datagram with no user data), A shutdown() to disable reading was previously done on the socket. The buffer length specified was zero.

What does recv () return?

If successful, recv() returns the length of the message or datagram in bytes. The value 0 indicates the connection is closed.

Why is recv blocking?

If a datagram packet is too long to fit in the supplied buffer, datagram sockets discard excess bytes. If data is not available for the socket socket, and socket is in blocking mode, the recv() call blocks the caller until data arrives.


3 Answers

recv will return when there is data in the internal buffers to return. It will not wait until there is 100 bytes if you request 100 bytes.

If you're sending 100 byte "messages", remember that TCP does not provide messages, it is just a stream. If you're dealing with application messages, you need to handle that at the application layer as TCP will not do it.

There are many, many conditions where a send() call of 100 bytes might not be read fully on the other end with only one recv call when calling recv(..., 100); here's just a few examples:

  • The sending TCP stack decided to bundle together 15 write calls, and the MTU happened to be 1460, which - depending on timing of the arrived data might cause the clients first 14 calls to fetch 100 bytes and the 15. call to fetch 60 bytes - the last 40 bytes will come the next time you call recv() . (But if you call recv with a buffer of 100 , you might get the last 40 bytes of the prior application "message" and the first 60 bytes of the next message)

  • The sender buffers are full, maybe the reader is slow, or the network is congested. At some point, data might get through and while emptying the buffers the last chunk of data wasn't a multiple of 100.

  • The receiver buffers are full, while your app recv() that data, the last chunk it pulls up is just partial since the whole 100 bytes of that message didn't fit the buffers.

Many of these scenarios are rather hard to test, especially on a lan where you might not have a lot of congestion or packet loss - things might differ as you ramp up and down the speed at which messages are sent/produced.

Anyway. If you want to read 100 bytes from a socket, use something like

int
readn(int f, void *av, int n)
{
    char *a;
    int m, t;

    a = av;
    t = 0;
    while(t < n){
        m = read(f, a+t, n-t);
        if(m <= 0){
            if(t == 0)
                return m;
            break;
        }
        t += m;
    }
    return t;
}

...

if(readn(mysocket,buffer,BUFFER_SZ) != BUFFER_SZ) {
  //something really bad is going on.

}
like image 157
nos Avatar answered Oct 31 '22 22:10

nos


The behavior is determined by two things. The recv low water mark and whether or not you pass the MSG_WAITALL flag. If you pass this flag the call will block until the requested number of bytes are received, even if the server crashes. Other wise it returns as soon as at least SO_RCVLOWAT bytes are available in the socket's receive buffer.

SO_RCVLOWAT

Sets the minimum number of bytes to process for socket input operations. The default value for SO_RCVLOWAT is 1. If SO_RCVLOWAT is set to a larger value, blocking receive calls normally wait until they have received the smaller of the low water mark value or the requested amount. (They may return less than the low water mark if an error occurs, a signal is caught, or the type of data next in the receive queue is different than that returned, e.g. out of band data). This option takes an int value. Note that not all implementations allow this option to be set.

like image 40
Robert S. Barnes Avatar answered Oct 31 '22 20:10

Robert S. Barnes


If you read the quote precisely, the most common scenario is:

  • the socket is receiving data. That 100 bytes will take some time.
  • the recv() call is made.
    • If there are more than 0 bytes in the buffer, recv() returns what is available and does not wait.
    • While there are 0 bytes available it blocks and the granularity of the threading system determines how long that is.
like image 2
Henk Holterman Avatar answered Oct 31 '22 22:10

Henk Holterman