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(¤tPayload, 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空闲超时时间(需要管理员权限):
- 打开注册表编辑器,定位到
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters - 创建或修改
DWORD类型的UDPIdleTime值,设置为更大的毫秒数(比如1800000表示30分钟) - 重启系统生效
3. 可选:改用recvfrom替代recv
UDP是无连接协议,使用recvfrom可以明确获取数据包的来源地址,确保只处理相机发送的数据包,同时避免潜在的地址关联问题:
// 替换原有的recv调用 recv_len = recvfrom(m_hStream, m_streamBuffer, sizeof(m_streamBuffer), 0, (struct sockaddr*)&fromCamera, &fromLen);
内容的提问来源于stack exchange,提问作者0b1001001
相关产品推荐
相关产品推荐

