5

I'm setting a timeout for the socket using SO_RCVTIMEO to 10 seconds. This question is specific to a stream socket (TCP). When I call recv(...) from what I can gather from the man pages, here is what I'm expecting:

  1. If remote closes the connection, it returns 0 immediately regardless of the timeout.
  2. If the timeout expires and no data was received, then we get a -1 returned and an errno of EAGAIN or EWOULDBLOCK.
  3. If an error occurs on the socket, it returns immediately with a -1 and then errno is set properly.
  4. If data becomes available, the socket waits until the timeout occurs before returning. This time it'll return in 10 seconds with the total bytes received.

Is this the correct behavior? I just want to make sure I'm properly understanding the docs.

Thanks! Brett

woolstar
  • 5,063
  • 20
  • 31
Brett
  • 4,066
  • 8
  • 36
  • 50

2 Answers2

6
  1. Correct.
  2. There are two different specifications (unfortunately I cannot test currently)
    2.1 ETIMEOUT would be returned. (EAGAIN or EWOULDBLOCK are returned on non-blocking sockets if no data is immediately available.) http://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html
    2.2 EAGAIN or EWOULDBLOCK would be returned for both possibilities mentioned under 2.1 http://man7.org/linux/man-pages/man2/recvmsg.2.html
  3. Correct.
  4. If at least 1 byte was read the socket might return anytime, even if less was read then told and the time-out did not yet expire.
alk
  • 69,737
  • 10
  • 105
  • 255
  • 2
    POSIX specifically says that for `SO_RCVTIMEO` and no data received, one of `EAGAIN` or `EWOULDBLOCK` is required: http://pubs.opengroup.org/onlinepubs/009695399/functions/setsockopt.html – torek Dec 16 '13 at 18:50
  • alk, care to comment on @torek's statement? Or do you have sources for #2? – Brett Dec 16 '13 at 18:58
  • @torek: It seems the information linked is outdated. Please see the current version here: http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html `EAGAIN` isn't mentioned anymore. – alk Dec 16 '13 at 20:16
  • @alk: following that link and clicking on "Use of Options" leads to http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_10_16 which does mention `EAGAIN` and `EWOULDBLOCK` (specifically for `SO_RCVTIMEO`). I'm not sure how one decides whether `SO_RCVTIMEO` is implemented at all, much less how to tell which POSIX doc outdates which other one, though. :-) – torek Dec 16 '13 at 20:33
0

Pretty much.

There is one caveat: if you set MSG_WAITALL, at least one implementation (FreeBSD) starts the timer over again every time some data arrive. For instance, if you ask to recv 8192 bytes and one byte arrives, the timer is reset. If another byte arrives within 10 seconds, the timer resets again. So if bytes trickle in one every 5 seconds, you'll wait (8191 * 5) = 40955 seconds = over 11 hours before recv finally returns 8192. (If not enough bytes arrive before EOF the recv fails with EAGAIN.)

The documentation implied (to me at least) that this occurred even without MSG_WAITALL, but testing demonstrates that this caveat applies only to the MSG_WAITALL case.

torek
  • 448,244
  • 59
  • 642
  • 775
  • To be honest - hard to imagine. Normally, I'd expect FreeBSD to conform to other implementations and to return as soon as some data are available. – glglgl Dec 16 '13 at 18:51
  • @glglgl: I admit I haven't tested it, but that's what the man page claims. Seems kind of broken. :-) – torek Dec 16 '13 at 18:51
  • Under all circumstances? I just [read](http://pubs.opengroup.org/onlinepubs/009695399/functions/recv.html) about the `recv()` option `MSG_WAITALL` which triggers this behaviour... – glglgl Dec 16 '13 at 18:55
  • @glglgl: quick scan of the source suggests that it's a lazy implementation. `sbwait()` uses the value in `so_timeo` and the receive loop falls into `sbwait, then goto restart` unless you set `MSG_DONTWAIT` or `MSG_NBIO` (or the socket itself is set `SS_NBIO`). You can set the high water mark (`so_rcv.sb_hiwat`) to stop the waiting earlier if needed though. – torek Dec 16 '13 at 18:59
  • 1
    This is not correct. If one byte arrives within the timeout, `recv()` *returns* with a return value of one. If you call it again, the timer is reset, of course. `recv()` makes no attempt to 'finally return 8192.' – user207421 Dec 16 '13 at 23:14
  • @EJP: I finally had a chance to test it, and you are correct (with caveat: setting `MSG_WAITALL` results in a very long delay followed by `recv` returning -1). – torek Dec 23 '13 at 06:16