Socket超时功能实现中是否需要两次调用WSAGetLastError()?
关于WSAGetLastError多次调用的问题解答
一、两次调用WSAGetLastError是否必要?
完全没有必要,反而存在逻辑风险。
WSAGetLastError返回的是当前线程中最后一次WinSock操作产生的错误码,它存储在线程本地空间中,同一线程内的后续WinSock/Win32函数调用会覆盖这个值。你代码中在printf和throw_exception里先后调用WSAGetLastError,虽然printf这类标准库函数通常不会修改WSA错误码,但这种依赖未定义行为的写法不可靠——如果后续代码调整,中间插入了其他Win32操作,第二次调用拿到的就不是最初的错误码了。
更严重的是,你的throw_exception函数存在逻辑错误:明明参数已经传入了错误码code,但函数内部却无视该参数,重新调用WSAGetLastError生成错误信息,这会导致异常中的错误码完全不可信。
二、“为了兼容多线程程序”的说法是否正确?
这个说法完全错误。
WSAGetLastError本身就是线程安全的,每个线程都有独立的错误码存储区域,不同线程的错误码不会互相干扰。多次调用不仅不能提升多线程兼容性,反而会增加错误码被意外覆盖的概率。
三、正确的写法建议
1. 修复throw_exception函数
直接使用传入的错误码,不再调用WSAGetLastError:
void throw_exception(const char *function, const int code, const char *content) { char message[128]{}; snprintf(message, sizeof(message), "%s[%d]: %s", function, code, content); throw std::runtime_error(message); }
2. 错误发生后立即保存错误码
在WinSock函数返回错误后,第一时间将错误码保存到变量中,后续所有错误处理都复用这个变量:
比如Connect函数中的这段代码:
// 原错误写法 if((err_code == SOCKET_ERROR) && (WSAGetLastError() != WSAEINTR)) { printf("Error: %d\n", WSAGetLastError()); throw_exception("Connect", WSAGetLastError(), "Failed to call select."); } // 修正后 if((err_code == SOCKET_ERROR)) { int err = WSAGetLastError(); if(err != WSAEINTR) { printf("Error: %d\n", err); throw_exception("Connect", err, "Failed to call select."); } }
其他所有两次调用WSAGetLastError的地方都按此方式修改,先保存、再复用。
3. 额外的代码问题修正
create_timeval函数中,微秒部分计算错误:应该将小数部分乘以1000000转换为微秒,否则超时时间会不符合预期:
tv.tv_usec = (long)(floatPart * 1000000);
getaddrinfo的错误处理错误:getaddrinfo失败时不会设置WSAGetLastError,而是直接返回错误码,应该用其返回值作为错误码:
int gai_err = getaddrinfo(remote_addr.c_str(), std::to_string(remote_port).c_str(), &hints, &result); if(gai_err != 0) { throw_exception("Connect", gai_err, "Failed to call getaddrinfo."); }
内容的提问来源于stack exchange,提问作者S-N
相关产品推荐
相关产品推荐

