【问题标题】:Can setting a timeout for posix recv cause lost udp packets?为 posix recv 设置超时会导致 udp 数据包丢失吗?
【发布时间】:2018-04-11 12:51:33
【问题描述】:

我发现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

设置超时值,该值指定输入函数在完成之前等待的最长时间。它接受一个时间值 具有指定秒数和微秒数的结构 限制等待输入操作完成的时间。如果一个 接收操作已经阻塞了这么长时间没有接收 附加数据,它应返回部分计数或 errno 设置为 [EAGAIN] 或 [EWOULDBLOCK] 如果没有接收到数据。这个的默认值 选项为零,表示接收操作不应 暂停。此选项采用 timeval 结构。请注意,并非所有 实现允许设置此选项。

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

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

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

【问题讨论】:

    标签: c++ sockets posix


    【解决方案1】:

    没有;它不会让你更容易丢包。

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

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

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

    【讨论】:

    • 这就是我所希望的。只有“......它将返回部分计数......”让我有点困惑,我仍然不明白它的含义
    • 嗯,也用 udp?然后我将不得不结束调用,因为在超时的情况下我想让线程做其他事情(只在很短的时间内),但如果有一个数据包等待完全接收,那么我需要那个数据包在做其他事情之前。总之非常感谢你的回答
    • @UKMonkey:不。您需要整个数据包来验证 UDP 校验和。
    • @user463035818 ...它应该返回部分计数.. 如果没有数据,则后面跟着 或设置为 [EAGAIN] 或 [EWOULDBLOCK] 的 errno收到。如果接收到数据,则调用正常进行,如果在延迟期间未接收到数据,则调用超时终止。你不能有超时和数据。
    • 关于“...它应返回部分计数...”与 SOCK_STREAM 类型相关(例如:TCP)。对于 SOCK_DGRAM 类型(例如 UDP),您将收到发送的 UDP 消息中的所有数据,或者没有数据。不会有部分计数。您获得的计数是发件人发送的大小。由于可以为任一类型的套接字设置 SO_RCVTIMEO 参数,因此描述涵盖了这两种情况。
    【解决方案2】:

    答案是否定的,丢失 UDP 数据将违反POSIX:

    recv() 函数应返回写入缓冲区参数指向的缓冲区的消息长度。 对于基于消息的套接字,例如 SOCK_DGRAM 和 SOCK_SEQPACKET,应在单个操作中读取整个消息。

    据推测,“部分计数”仅在使用 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% 由规范保证的

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-14
      • 1970-01-01
      • 2021-10-18
      • 2011-12-19
      • 2015-06-30
      • 2015-05-31
      • 1970-01-01
      相关资源
      最近更新 更多