gpt4 book ai didi

c - WSAConnectByName 超时

转载 作者:行者123 更新时间:2023-11-30 15:03:43 24 4
gpt4 key购买 nike

我正在尝试使用 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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com