- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 WSAConnectByName()
连接到某个地址。但是,它似乎忽略了 timeout
参数。
以下是 MS 示例中的代码(仅稍作修改):
SOCKET ConnSocket = INVALID_SOCKET;
int iResult;
BOOL bSuccess;
SOCKADDR_STORAGE LocalAddr = {0};
SOCKADDR_STORAGE RemoteAddr = {0};
DWORD dwLocalAddr = sizeof(LocalAddr);
DWORD dwRemoteAddr = sizeof(RemoteAddr);
ConnSocket = socket(AF_INET, SOCK_STREAM, 0);
if (ConnSocket == INVALID_SOCKET){
wprintf(L"socket failed with error: %d\n", WSAGetLastError());
return INVALID_SOCKET;
}
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
bSuccess = WSAConnectByName(ConnSocket, NodeName,
PortName, &dwLocalAddr,
(SOCKADDR*)&LocalAddr,
&dwRemoteAddr,
(SOCKADDR*)&RemoteAddr,
(struct timeval *)&tv,
NULL);
if (!bSuccess){
wprintf(L"WsaConnectByName failed with error: %d\n", WSAGetLastError());
closesocket(ConnSocket);
return INVALID_SOCKET;
}
当我使用不存在的地址(例如本地 IP 地址)时,代码不会因超时而失败,而是会停止,直到发生其他超时。
知道这里发生了什么吗?
最佳答案
这是由于文档不正确造成的。
timeout
参数仅部分使用。 WSAConnectByName
是一个复杂的函数,它在内部执行许多操作。
开头存在这样的代码:
ULONG time, ms;
if (timeout)
{
time = GetTickCount();
ms = timeout->tv_sec * 1000 + timeout->tv_usec/1000;
}
然后多次调用如下代码:
if (timeout && GetTickCount() - time > ms) return WSAETIMEDOUT;
但函数的核心调用 ConnectEx
像这样:
if (!ConnectEx(*))
{
if (GetLastError() == ERROR_IO_PENDING)
{
WSAGetOverlappedResult(*); // you wait here and the timeout is not used!
}
}
两者ConnectEx
(这是一个异步函数)和GetOverlappedResult
没有指定超时的参数。你最终会在 GetOverlappedResult
等待之后ConnectEx
退出,但不能为其设置超时。
只有一个好的解决方案 - 使用 ConnectEx
直接,不使用任何超时。
还有一个问题点 - GetAddrInfoEx
用于将 nodename
转换为 IP 地址。该函数使用 timeout=0, lpOverlapped=0, lpCompletionRoutine = 0
调用 - 所以这里也可以等待 DNS 请求。仅从 Windows 8 开始支持异步查询
编辑
如果直接使用ConnectEx
我们可以使用超时(感谢 Remy Lebeau 的想法) - 创建/使用 OVERLAPPED.hEvent
和
OVERLAPPED Overlapped = {};
Overlapped.hEvent = CreateEvent(0, 0, 0, 0);
//... ConnectEx ...
ULONG NumberOfBytesTransferred = 0;
ULONG err = GetLastError();
if (err == ERROR_IO_PENDING)
{
switch (WaitForSingleObject(Overlapped.hEvent, ms))
{
case STATUS_TIMEOUT:
CancelIoEx((HANDLE)s, &Overlapped);
err = WSAETIMEDOUT;
break;
case WAIT_OBJECT_0:
// really final NT status of operation is Internal and NumberOfBytesTransferred == InternalHigh
// GetOverlappedResult set LastError by translating (NTSTATUS)Internal to win32 error
// with the loss of accuracy, especially if status > 0
GetOverlappedResult((HANDLE)s, &Overlapped, &NumberOfBytesTransferred, TRUE);
err = GetLastError();
// code of GetOverlappedResult in this case for clarity
//if ((NTSTATUS)Overlapped.InternalHigh == STATUS_PENDING)
//{
// WaitForSingleObject(Overlapped.hEvent, INFINITE);
//}
//NumberOfBytesTransferred = (ULONG)Overlapped.InternalHigh;
//if (0 > (NTSTATUS)Overlapped.Internal)
//{
// SetLastError(RtlNtStatusToDosError((NTSTATUS)Overlapped.Internal));
//}
break;
default: __debugbreak();
}
}
或替代用途GetOverlappedResultEx
与我们的超时(但需要 Windows 8+)
或者最好的选择(在我看来)-使用 BindIoCompletionCallback((HANDLE)s,)
或者直接为套接字绑定(bind)自 IOCP,并且在调用后根本不等待。
关于c - WSAConnectByName 超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40614613/
我正在尝试使用 WSAConnectByName() 连接到某个地址。但是,它似乎忽略了 timeout 参数。 以下是 MS 示例中的代码(仅稍作修改): SOCKET ConnSocket = I
我是一名优秀的程序员,十分优秀!