gpt4 book ai didi

winapi - 如何使用 BindIoCompletionCallback 检测 WinSock TCP 超时

转载 作者:行者123 更新时间:2023-12-02 06:56:17 25 4
gpt4 key购买 nike

我正在使用 BindIoCompletionCallback 构建 Visual C++ WinSock TCP 服务器,它可以正常接收和发送数据,但我找不到检测超时的好方法:SetSockOpt/SO_RCVTIMEO/SO_SNDTIMEO 对非阻塞套接字没有影响,如果对等方没有发送任何数据,CompletionRoutine 根本没有被调用。

我正在考虑将 RegisterWaitForSingleObject 与 OVERLAPPED 的 hEvent 字段一起使用,这可能有效,但根本不需要 CompletionRoutine,我还在使用 IOCP 吗?如果我仅使用 RegisterWaitForSingleObject 而不使用 BindIoCompletionCallback 是否存在性能问题?

更新:代码示例:

我的第一次尝试:

    bool CServer::Startup() {
SOCKET ServerSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
WSAEVENT ServerEvent = WSACreateEvent();
WSAEventSelect(ServerSocket, ServerEvent, FD_ACCEPT);
......
bind(ServerSocket......);
listen(ServerSocket......);
_beginthread(ListeningThread, 128 * 1024, (void*) this);
......
......
}

void __cdecl CServer::ListeningThread( void* param ) // static
{
CServer* server = (CServer*) param;
while (true) {
if (WSAWaitForMultipleEvents(1, &server->ServerEvent, FALSE, 100, FALSE) == WSA_WAIT_EVENT_0) {
WSANETWORKEVENTS events = {};
if (WSAEnumNetworkEvents(server->ServerSocket, server->ServerEvent, &events) != SOCKET_ERROR) {
if ((events.lNetworkEvents & FD_ACCEPT) && (events.iErrorCode[FD_ACCEPT_BIT] == 0)) {
SOCKET socket = accept(server->ServerSocket, NULL, NULL);
if (socket != SOCKET_ERROR) {
BindIoCompletionCallback((HANDLE) socket, CompletionRoutine, 0);
......
}
}
}
}
}
}

VOID CALLBACK CServer::CompletionRoutine( __in DWORD dwErrorCode, __in DWORD dwNumberOfBytesTransfered, __in LPOVERLAPPED lpOverlapped ) // static
{
......
BOOL res = GetOverlappedResult(......, TRUE);
......
}

class CIoOperation {
public:
OVERLAPPED Overlapped;
......
......
};

bool CServer::Receive(SOCKET socket, PBYTE buffer, DWORD length, void* context)
{
if (connection != NULL) {
CIoOperation* io = new CIoOperation();
WSABUF buf = {length, (PCHAR) buffer};
DWORD flags = 0;
if ((WSARecv(Socket, &buf, 1, NULL, &flags, &io->Overlapped, NULL) != 0) && (GetLastError() != WSA_IO_PENDING)) {
delete io;
return false;
} else return true;
}
return false;
}

正如我所说,如果客户端实际上正在向我发送数据,“接收”不会阻塞,CompletionRoutine 被调用,数据已收到,那么它工作正常,但如果客户端没有向我发送任何数据,那么这里有一个陷阱,超时后如何放弃?

由于 SetSockOpt/SO_RCVTIMEO/SO_SNDTIMEO 在这里没有帮助,我想我应该使用 OVERLAPPED 结构中的 hEvent 字段,该字段将在 IO 完成时发出信号,但其上的 WaitForSingleObject/WSAWaitForMultipleEvents 将阻止 Receive 调用,我想要Receive 总是立即返回,因此我使用了 RegisterWaitForSingleObject 和 WAITORTIMERCALLBACK。它起作用了,回调在超时后被调用,或者 IO 完成,但现在我对任何单个 IO 操作都有两个回调:CompletionRoutine 和 WaitOrTimerCallback:

如果IO完成,它们将同时被调用,如果IO未完成,WaitOrTimerCallback将被调用,然后我调用CancelIoEx,这导致CompletionRoutine被调用并出现一些ABORTED错误,但这里是一个竞争条件,也许IO会在我取消它之前完成,然后...等等,总而言之,它相当复杂。

然后我意识到我实际上根本不需要 BindIoCompletionCallback 和 CompletionRoutine,并且从 WaitOrTimerCallback 中执行所有操作,它可能会起作用,但这是一个有趣的问题,我首先想构建一个基于 IOCP 的 Winsock 服务器,并且认为 BindIoCompletionCallback 是最简单的方法,使用 Windows 本身提供的线程池,现在我最终得到一个根本没有 IOCP 代码的服务器?还是IOCP吗?或者我应该忘记 BindIoCompletionCallback 并构建我自己的 IOCP 线程池实现?为什么?

最佳答案

我所做的是强制超时/完成通知进入套接字对象中的关键部分。一旦进入,获胜者就可以设置套接字状态变量并执行其操作,无论是什么。如果 I/O 完成首先到达,则 I/O 缓冲区阵列将以正常方式处理,并且状态机将指示任何超时重新启动。类似地,如果首先出现超时,则 I/O 会被 CancelIOEx 处理,并且任何稍后排队的完成通知都会被状态引擎丢弃。由于这些可能的“迟到”通知,我将释放的套接字放入超时队列中,并仅在五分钟后将它们回收到套接字对象池中,这与 TCP 堆栈本身将其套接字放入“TIME_WAIT”的方式类似。

为了实现超时,我有一个线程对超时对象的 FIFO 增量队列进行操作,每个超时限制有一个队列。线程在输入队列上等待新对象,超时时间根据队列头部对象的最小超时到期时间计算。

服务器中只使用了一些超时,因此我使用了在编译时固定的队列。通过向线程输入队列发送适当的“命令”消息并与新套接字混合来添加新队列或修改超时是相当容易的,但我还没有做到这一点。

超时后,线程在对象中调用一个事件,如果是套接字,则该事件将进入套接字对象 CS 保护的状态机(这些是套接字派生的 TimeoutObject 类) .

更多:

我等待控制超时线程输入队列的信号量。如果收到信号,我会从输入队列中获取新的 TimeoutObject,并将其添加到它要求的任何超时队列的末尾。如果信号量等待超时,我会检查超时 FIFO 队列头部的项目,并通过从超时时间中减去当前时间来重新计算它们的剩余间隔。如果间隔为 0 或负数,则会调用超时事件。在迭代队列及其头部时,我将下一次超时之前的最小剩余间隔保留在本地。如果所有队列中的所有头项都有非零剩余间隔,我会使用累积的最小剩余间隔返回等待队列信号量。

事件调用返回一个枚举。此枚举指示超时线程如何处理刚刚触发其事件的对象。一种选择是通过重新计算超时时间并在最后将对象推回其超时队列来重新启动超时。

我没有使用 RegisterWaitForSingleObject(),因为它需要 .NET,而且我的 Delphi 服务器都是非托管的(我很久以前就编写了我的服务器!)。

因为,IIRC,它有 64 个句柄的限制,就像 WaitForMultipleObjects() 一样。我的服务器有超过 23000 个客户端超时。我发现单个超时线程和多个 FIFO 队列更加灵活 - 任何旧对象都可以在其上超时,只要它是 TimeoutObject 的后代 - 不需要额外的操作系统调用/句柄。

关于winapi - 如何使用 BindIoCompletionCallback 检测 WinSock TCP 超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8686818/

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