2

I found this answer on how to set a timeout for posix socket. The linux part of that answer:

// LINUX
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

and the quote from the posix documentation:

SO_RCVTIMEO

Sets the timeout value that specifies the maximum amount of time an input function waits until it completes. It accepts a timeval structure with the number of seconds and microseconds specifying the limit on how long to wait for an input operation to complete. If a receive operation has blocked for this much time without receiving additional data, it shall return with a partial count or errno set to [EAGAIN] or [EWOULDBLOCK] if no data is received. The default for this option is zero, which indicates that a receive operation shall not time out. This option takes a timeval structure. Note that not all implementations allow this option to be set.

What I dont understand is: Can this cause loosing udp packages? What if the timeout is reached while a udp package is received?

Also related: setting timeout for recv fcn of a UDP socket

PS: I am aware that UDP is inherently unreliable, so my question is really mainly about the case where the timeout occurs while an udp message is processed.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185

2 Answers2

6

No; it doesn't make you more likely to drop packets.

Looking at how network transport happens at a lower level; you have a network card. As this card receives data, irrespective of what your program is doing, it stores the data into it's own memory area. When you call recv; you're asking the OS to move data from the network cards memory to your programs memory. This means that if a packet comes in while your thread is doing something else; it isn't just dropped, but processed the next time your thread comes to get data.

If your thread doesn't call recv often enough; then the memory for the network card will become full. When this happens no new packets can be stored; and if it's using TCP then the router will be told that it's not able to process it; if it's UDP then it will simply be dropped. It is this part that makes UDP inherently unreliable as it can happen at any point during the transport of the packet.

The timeout impacts how long the thread will wait for data to appear in the networkcard memory area; and unless you never call recv again; does not impact dropped packets or not.

UKMonkey
  • 6,941
  • 3
  • 21
  • 30
  • 1
    thats what i was hoping for. Only the "...it shall return with a partial count.." confused me a bit and I still dont really understand what it means – 463035818_is_not_an_ai Apr 11 '18 at 13:29
  • hm, also with udp? then I will have to wrap the call, because in case of timeout I want to make the thread do something else (only for a tiny amount of time), but in case there is a packet waiting to be completely received then i need that packet before doing the something else. Anyhow thanks a lot for the answer – 463035818_is_not_an_ai Apr 11 '18 at 13:35
  • 1
    @UKMonkey: Nope. You need the whole packet to verify the UDP checksum. – MSalters Apr 12 '18 at 08:49
  • @MSalters that said; the underlying mechanism could decide to send your data as 2 packets rather than one ... I think ... – UKMonkey Apr 12 '18 at 09:39
  • 2
    @user463035818 *...it shall return with a partial count..* is followed by *or errno set to [EAGAIN] or [EWOULDBLOCK] if no data is received*. If data is received then the call behave as usual, and if no data received during the delay call is terminated in timeout. You can't have **timeout and data**. – Jean-Baptiste Yunès Apr 12 '18 at 15:22
  • @MSalters -- TCP send/recv can send your data as any number of packets, and when you call recv, the amount you receive can be different from the original send() call size. Usually it's 1:1, but it's not guaranteed; you must always be ready to read more data if you get less than you are expecting. – JoGusto Jul 08 '19 at 20:14
  • 1
    regarding "...it shall return partial count..." is relevant for SOCK_STREAM types (eg: TCP). For SOCK_DGRAM types (eg UDP) you will either receive all the data in the sent UDP message, or no data. There won't be a partial count. The count you get is the size the sender sent. Since the SO_RCVTIMEO parameter can be set for either type of socket, the description covers both circumstances. – JoGusto Jul 08 '19 at 20:18
1

The answer is no, losing UDP data would be in violation of POSIX:

The recv() function shall return the length of the message written to the buffer pointed to by the buffer argument. For message-based sockets, such as SOCK_DGRAM and SOCK_SEQPACKET, the entire message shall be read in a single operation.

Presumably, the "partial count" only happens is a connection-based socket when the MSG_WAITALL option is used.

That being said, the use of SO_RECVTIMEO is generally frowned upon, and the "proper" way to implement timeouts on sockets is by using nonblocking sockets and select(). This is for historical reasons, and not because setting a timeout is somehow inherently bad design or something. If you insist on using SO_RECVTIMEO, be aware of potential portability problems:

  • POSIX mentions SO_RECVTIMEO, but does not require it
  • On Windows, a timeout in rcv() will put the socket in a bad state and you should close it immediately afterwards. On POSIX you can (in my experience) still use the socket after a timeout caused by SO_RECVTIMEO, but one could argue this is not 100% guaranteed by the spec
FrancoVS
  • 262
  • 1
  • 7