C++ WinSock环境下如何为recv函数设置超时避免永久阻塞
WinSock为recv设置接收超时的实现方案
方法1:使用setsockopt设置套接字接收超时(最简单直接)
通过SO_RCVTIMEO选项为已连接的客户端套接字设置单次recv调用的最大等待时间,超时后recv会返回错误,不会永久阻塞。
在accept拿到客户端套接字后、进入接收循环前添加如下代码:
// 设置2秒接收超时 DWORD recvTimeout = 2000; // 单位为毫秒 if (setsockopt(clientSock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&recvTimeout, sizeof(recvTimeout)) == SOCKET_ERROR) { printf("设置接收超时失败,错误码:%d\n", WSAGetLastError()); closesocket(clientSock); continue; }
修改原有recv逻辑,补充返回值判断:
int recvLen; while(1) { recvLen = recv(clientSock, &requestStr[0], bufSize, 0); // 接收出错判断 if (recvLen == SOCKET_ERROR) { int errCode = WSAGetLastError(); if (errCode == WSAETIMEDOUT) { printf("接收超时,跳出循环\n"); // 此处可自行处理已接收到的data数据 } else { printf("接收出错,错误码:%d\n", errCode); } break; } // 客户端主动断开连接判断 if (recvLen == 0) { printf("客户端已断开连接\n"); break; } // 仅拼接实际收到的字节,避免缓冲区残留旧数据污染结果 data.append(requestStr, 0, recvLen); // 原有结束符判断可优化,无需硬编码检查每个位置 if (data.find('}') != string::npos) { break; } // 原有GET请求判断逻辑 if (recvLen >=4 && requestStr[0] == 'G' && requestStr[1] == 'E' && requestStr[2] == 'T' && requestStr[3] == ' ') { getFlag = true; for(int i = 0; i < 20; i++) { recvLen = recv(clientSock, &requestStr[0], bufSize, 0); if (recvLen <= 0) break; data.append(requestStr, 0, recvLen); } break; } }
方法2:使用select模型实现超时监听(更灵活)
如果需要更精细的控制逻辑,比如同时处理多个连接、自定义超时规则,可以用select先监听套接字的可读状态,超时后直接返回:
while(1) { fd_set readFdSet; FD_ZERO(&readFdSet); FD_SET(clientSock, &readFdSet); timeval timeout = {2, 0}; // 超时2秒,第一个参数为秒,第二个为微秒 int selectRet = select(0, &readFdSet, NULL, NULL, &timeout); if (selectRet == 0) { printf("接收超时,跳出循环\n"); break; } if (selectRet == SOCKET_ERROR) { printf("select调用出错,错误码:%d\n", WSAGetLastError()); break; } // 确认有数据可读再调用recv,此时recv不会阻塞 int recvLen = recv(clientSock, &requestStr[0], bufSize, 0); // 后续recv返回值判断、数据处理逻辑和方法1完全一致 }
补充注意事项
- 上述两种方法默认设置的是单次等待数据的超时,如果需要限制整个接收流程的总时长,可以在进入接收循环前记录起始时间戳,每次循环判断当前时间和起始时间的差值,超过阈值直接跳出即可。
- 原有代码未判断recv返回值,容易出现客户端断开后程序死循环、拼接无效数据的问题,建议按照示例补充判断逻辑。
内容的提问来源于stack exchange,提问作者Expzwei
相关产品推荐
相关产品推荐

