Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

recv() with length zero is valid?

Tags:

c

sockets

Using the recv() function in C to read from a 'stream' socket, can the len parameter be zero?

The recv() function returns zero for 'remote connection closed' and the number of bytes actually read on normal operation, so it sounds problematic if it should read zero bytes.

P.S.
Yes I know to deal with it separately, and not get to this situation, still I'm wondering if the function can handle it, I can't find any documentation about it.

like image 433
Eran Avatar asked May 13 '11 16:05

Eran


People also ask

What does recv () return?

RETURN VALUE 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.

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.

Why is recv blocking?

If data is not available for the socket socket, and socket is in blocking mode, the recv() call blocks the caller until data arrives. If data is not available and socket is in nonblocking mode, recv() returns a -1 and sets the error code to EWOULDBLOCK.

What does RECV return when there is still data in the buffer but the other side has closed the socket?

If no error occurs, recv returns the number of bytes received and the buffer pointed to by the buf parameter will contain this data received. If the connection has been gracefully closed, the return value is zero.


2 Answers

The documentation for recv on my system (Linux) says

If no messages are available at the socket, the receive calls wait for a message to arrive

and

If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on the type of socket the message is received from.

Based on the documentation, I would expect my recv to wait for a message, then effectively discard it (UDP) or leave it in the stream (TCP).

This could be used to test if a non-blocking TCP socket has data waiting.

Update: Testing reveals that this interpretation of the documentation is accurate.

Server:

$ perl -MIO::Socket::INET -E'
   my $s = IO::Socket::INET->new(Listen => 1) or die $!;
   say $s->sockport;

   my $c = $s->accept or die $!;
   say "[".localtime."] connected";

   $c->recv(my $buf, 0) // die $!;
   say "[".localtime."] received";

   say <$c>;
'
39493
[Fri May 13 13:49:53 2011] connected
[Fri May 13 13:49:55 2011] received
foo

Client:

$ perl -MIO::Socket::INET -E'
   my $s = IO::Socket::INET->new(
      PeerAddr => "127.0.0.1",
      PeerPort => $ARGV[0],
   ) or die $!;
   sleep 2;
   say $s "foo";
' 39493

(These Perl functions are just thin interfaces to the system calls. Feel free to rewrite them in C.)

like image 54
ikegami Avatar answered Sep 27 '22 18:09

ikegami


I believe the answer is "it depends". If it is not specified by the standard (and indeed I believe it isn't) any implementation could do as it pleases.

  • It could fail with EINVAL
  • It could hang
  • It could return 0 and go on
  • It could print a funny message and start the game rogue (I understand gcc used to do that :)) )

Actually on my implementation, it returns 0 and goes on. To check if it failed or simply returned 0, you can check errno after the call, so it's not as problematic as you may think.

like image 36
cnicutar Avatar answered Sep 27 '22 20:09

cnicutar