You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SO_RCVTIMEO用于两次recvfrom()调用时出现立即超时问题如何解决?

UDP接收超时二次触发问题的解决方案

核心问题

你遇到的问题根源有两个:

  1. socket描述符不匹配:你用sock_temp设置超时,但接收循环用的是sockfd——超时设置根本没作用到实际接收的socket上。
  2. SO_RCVTIMEO的动态计时特性:内核会维护超时的剩余时间,第一次超时触发后,这个剩余时间会被更新为接近0的值,第二次调用recvfrom时会直接使用这个剩余时间,导致立即超时。即使你重新设置超时,若没覆盖内核的动态值,或者用错了socket,也不会生效。

修正步骤

1. 确保超时设置到正确的socket

把setsockopt的第一个参数改成接收用的sockfd,而非sock_temp。

2. 每次接收循环前重置完整超时时间

在进入第二个接收循环前,重新设置完整的超时值,覆盖内核维护的剩余时间。

修正后的代码示例

#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

// 假设timeout是已定义的超时秒数,比如5
int timeout = 5;
int sockfd; // 实际用于接收的socket描述符
struct sockaddr addr;
socklen_t addr_len = sizeof(addr);

int main() {
    // ... 初始化socket的代码 ...

    struct timeval tv;
    // 第一次设置超时(给第一个循环用)
    tv.tv_sec = timeout;
    tv.tv_usec = 0;
    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0) {
        perror("Setting socket timeout failed");
        exit(EXIT_FAILURE);
    }

    // 第一个接收循环:处理第一种UDP包
    char buffer1[1024];
    int packet_num1 = 10;
    for (int i = 0; i < packet_num1; i++) {
        int n = (int)recvfrom(sockfd, buffer1, sizeof(buffer1), 0, (struct sockaddr*)&addr, &addr_len);
        if (n < 0) {
            if (errno == EWOULDBLOCK || errno == EAGAIN) {
                printf("First loop receive timeout\n");
                break;
            } else {
                perror("First loop recvfrom failed");
                exit(EXIT_FAILURE);
            }
        }
        // 处理收到的第一个类型数据包
    }

    // 进入第二个循环前,重置完整超时时间
    tv.tv_sec = timeout;
    tv.tv_usec = 0;
    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0) {
        perror("Resetting socket timeout failed");
        exit(EXIT_FAILURE);
    }

    // 第二个接收循环:处理第二种UDP包
    char buffer2[1024];
    int packet_num2 = 10;
    for (int i = 0; i < packet_num2; i++) {
        int n = (int)recvfrom(sockfd, buffer2, sizeof(buffer2), 0, (struct sockaddr*)&addr, &addr_len);
        if (n < 0) {
            if (errno == EWOULDBLOCK || errno == EAGAIN) {
                printf("Second loop receive timeout\n");
                break;
            } else {
                perror("Second loop recvfrom failed");
                exit(EXIT_FAILURE);
            }
        }
        // 处理收到的第二个类型数据包
    }

    // ... 后续代码 ...
    return 0;
}

关键细节说明

  • errno检查时机:只有当recvfrom返回-1时,才需要检查errno,函数成功时errno的值是未定义的,提前检查会导致错误判断。
  • SO_RCVTIMEO的本质:这个选项设置的是累计超时时间,而非单次调用的超时。内核会持续计时,直到超时触发或收到数据,触发后剩余时间会被保留,必须手动重置才能恢复初始超时时长。

内容的提问来源于stack exchange,提问作者Chris C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 16:23:18