为 posix recv 设置超时会导致丢失 udp 数据包吗?

Can setting a timeout for posix recv cause lost udp packets?

我发现 this answer 如何为 posix 套接字设置超时。该答案的 linux 部分:

// 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);

以及 posix 文档中的引述:

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.

我不明白的是:这会导致丢失 udp 包吗? 收到udp包超时怎么办?

还相关:setting timeout for recv fcn of a UDP socket

PS:我知道UDP本质上是不可靠的,所以我的问题实际上主要是关于处理udp消息时发生超时的情况。

否;它不会让你更有可能丢包。

查看网络传输如何在较低级别发生;你有网卡。当此卡接收数据时,无论您的程序在做什么,它都会将数据存储到自己的内存区域中。当你打电话给 recv;您要求 OS 将数据从网卡内存移动到您的程序内存。这意味着如果您的线程正在做其他事情时有一个数据包进来;它不仅会被丢弃,还会在您的线程下次获取数据时进行处理。

如果您的线程没有足够频繁地调用 recv;那么网卡的内存就会变满。发生这种情况时,无法存储新数据包;如果它使用 TCP,那么路由器将被告知它无法处理它;如果它是 UDP,那么它将被简单地丢弃。正是这一部分使 UDP 本质上不可靠,因为它可能在数据包传输过程中的任何时候发生。

超时会影响线程等待数据出现在网卡内存区域的时间;除非你再也不打电话给 recv;不影响丢弃的数据包。

答案是否定的,丢失 UDP 数据将违反 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.

据推测,当使用 MSG_WAITALL 选项时,“部分计数”只会发生在基于连接的套接字上。

话虽这么说,SO_RECVTIMEO 的使用通常不受欢迎,在套接字上实现超时的“正确”方法是使用非阻塞套接字和 select()。这是出于历史原因,而不是因为设置超时在某种程度上本质上是糟糕的设计或其他原因。如果您坚持使用 SO_RECVTIMEO,请注意潜在的可移植性问题:

  • POSIX 提及 SO_RECVTIMEO,但 does not require it
  • 在 Windows 上,rcv() 中的超时将 put the socket in a bad state,您应该在之后立即关闭它。在 POSIX 上,您可以(根据我的经验)在 SO_RECVTIMEO 引起的超时后仍然使用套接字,但有人可能会争辩说这不是规范
  • 100% 保证的