1

我在 Windows 上使用 IOCP。以前我使用方法GetQueuedCompletionStatus来轮询队列,一切都很好。但是当我决定重构逻辑以使用完成例程WSARecv调用时,它总是失败并出现错误WSAEINVAL(10022)。此代码在创建的线程中CreateTread

int flags = 0;
m_iocport = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0);
handle = CreateIoCompletionPort(clientSocket, m_iocport, 0, 0);
OVERLAPPED_EX *over = new OVERLAPPED_EX();
result = WSARecv(clientSocket, &over->m_wsabuf, 1, NULL, &flags, over, WorkerRoutine);

并且工作例程是空的并且具有以下定义:

void static CALLBACK WorkerRoutine(DWORD Error, DWORD BytesTransferred, LPWSAOVERLAPPED Overlapped, DWORD InFlags) {}

当我传递NULL而不是传递WorkerRoutine给 WSARecv 方法时,一切正常。但是当我将完成例程传递给调用时,它失败并出现错误 10022。我尝试使用WorkerRoutine&WorkerRoutine没有任何帮助。

hEvent属性在 OVERLAPPED_EX 对象中设置为 NULL。

4

1 回答 1

4

与文件关联的 I/O 完成端口,lpCompletionRoutine这是互斥参数。你不能同时使用它。当你这样做时 - 内核返回STATUS_INVALID_PARAMETER被翻译成WSAEINVAL. 所以你必须得到这个错误。

IOCP 和 ApcRoutine 2 种不同的方式通知您操作完成。当您使用 IOCP 时 - 系统在完成时将数据包发送到 IOCP。您稍后需要使用GetQueuedCompletionStatus或提取此数据包。NtRemoveIoCompletion这不是“民意调查”。ApcRoutine如果您使用系统插入 apc 到您的线程,则从另一侧。一次和并发使用两种方式进行通知 -STATUS_INVALID_PARAMETER在这种情况下,当返回给您时,这是逻辑错误和内核正确的做法。

不幸的是,这在 MSDN 中并不清楚,或者我无法找到它。但一些研究 + WRK 源代码有助于理解这种情况:

WSARecv内部调用ZwDeviceIoControlFile,结果内核调用IopXxxControlFile. 当参数 for存在并且您在lpCompletionRoutine != 0一点上完全失败时:ApcRoutineIopXxxControlFile

    //
    // If this file has an I/O completion port associated w/it, then ensure
    // that the caller did not supply an APC routine, as the two are mutually
    // exclusive methods for I/O completion notification.
    //

    if (fileObject->CompletionContext && IopApcRoutinePresent( ApcRoutine )) {
        ObDereferenceObject( fileObject );
        return STATUS_INVALID_PARAMETER;
}

您也可以在堆栈中分配 m_wsabuf。有效的必须只是缓冲区到哪一点WSABUF

如果此函数以重叠方式完成,则 Winsock 服务提供者有责任在此调用返回之前捕获WSABUF 结构。这使应用程序能够构建由lpBuffers 参数指向的基于堆栈的WSABUF数组。

所以你可以OVERLAPPED_EX只缓冲到哪一点WSABUF.buf而不是整体WSABUF。但这已经与您的错误无关

于 2017-10-23T18:39:26.950 回答