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

WinSock2 UDP Socket长时间无数据后无法读取数据求助

问题分析与解决方案

核心问题1:SO_RCVTIMEO参数设置错误

Windows平台下,SO_RCVTIMEO选项要求传入**struct timeval结构体**,而非直接传入毫秒数的int值。你的代码中直接传递int类型的超时时间,会导致内存结构不匹配,超时设置无法正确生效,进而引发长时间空闲后的异常行为。

核心问题2:Windows默认UDP空闲超时机制

Windows系统默认的UDPIdleTime参数为600秒(10分钟),若UDP Socket在这段时间内没有任何收发操作,系统会释放该Socket的关联资源。此时即使网卡收到了相机的数据包(Wireshark能捕获),Socket也无法读取这些数据,最终返回WSAETIMEDOUT错误。


具体修复步骤

1. 修正SO_RCVTIMEO的设置代码

将原有的超时设置代码替换为使用struct timeval的正确实现:

// 替换原有的setsockopt(SO_RCVTIMEO)调用
struct timeval recvTimeout;
recvTimeout.tv_sec = receiveTimeout_ms / 1000;
recvTimeout.tv_usec = (receiveTimeout_ms % 1000) * 1000;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&recvTimeout, sizeof(recvTimeout)) == SOCKET_ERROR) {
    LOG_ERROR(Logger::sdkLogger(), "Failed to set SO_RCVTIMEO: " << WSAGetLastError());
}

同时建议检查所有setsockopt的返回值,避免忽略设置失败的情况。

2. 规避UDPIdleTime限制(二选一)

方案A:定期发送心跳包

在Socket空闲期间,每隔小于10分钟的时间(比如5分钟)向相机发送一个空的UDP数据包,保持Socket的活跃状态,避免系统释放资源:

// 在run循环中添加心跳逻辑
DWORD lastHeartbeatTime = GetTickCount();
constexpr DWORD HEARTBEAT_INTERVAL = 5 * 60 * 1000; // 5分钟

while (!m_shutdown) 
{
    // 检查是否需要发送心跳
    DWORD currentTime = GetTickCount();
    if (currentTime - lastHeartbeatTime >= HEARTBEAT_INTERVAL) {
        sendto(m_hStream, "", 0, 0, (struct sockaddr*)&fromCamera, fromLen);
        lastHeartbeatTime = currentTime;
    }

    // 原有的recv逻辑
    if ((recv_len = recv(m_hStream, m_streamBuffer, sizeof(m_streamBuffer),0)) != SOCKET_ERROR) 
    {
        m_Decoder->decode(&currentPayload, recv_len, (vcxInt32*)m_streamBuffer);
        lastHeartbeatTime = currentTime; // 收到数据后重置心跳计时
    }
    else
    {
        // 原有的错误处理逻辑
        auto error = WSAGetLastError();
        int socketError;
        int len = sizeof(socketError);
        int socketState = getsockopt(m_hStream, SOL_SOCKET, SO_ERROR, (char*)&socketError, &len);
        if (socketState == 0 && socketError == 0)
        {
            LOG_DEBUG(Logger::sdkLogger(), "VcxCamera::run(). Error " << error << " occurred, but socket still readable");
        }
        else
        {
            LOG_DEBUG(Logger::sdkLogger(), "VcxCamera::run(). Error " << error << " occurred, socket not usable (" << socketError << ").");
        }
    }
}

方案B:修改系统UDPIdleTime参数

通过修改注册表调整UDP空闲超时时间(需要管理员权限):

  1. 打开注册表编辑器,定位到HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
  2. 创建或修改DWORD类型的UDPIdleTime值,设置为更大的毫秒数(比如1800000表示30分钟)
  3. 重启系统生效

3. 可选:改用recvfrom替代recv

UDP是无连接协议,使用recvfrom可以明确获取数据包的来源地址,确保只处理相机发送的数据包,同时避免潜在的地址关联问题:

// 替换原有的recv调用
recv_len = recvfrom(m_hStream, m_streamBuffer, sizeof(m_streamBuffer), 0, 
                    (struct sockaddr*)&fromCamera, &fromLen);

内容的提问来源于stack exchange,提问作者0b1001001

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 02:43:41