我在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
没有任何帮助。
OVERLAPPED_EX对象中的hEvent
属性设置为NULL。
答案 0 :(得分:2)
与文件关联的I / O完成端口和lpCompletionRoutine
这是互斥参数。你不能同时使用它。当你这样做 - 内核返回STATUS_INVALID_PARAMETER
,它被转换为WSAEINVAL
。所以你必须得到这个错误。
IOCP和ApcRoutine 2通知您有关操作完成的不同方式。当您使用IOCP时 - 系统将数据包发送到IOCP完成。您需要稍后使用GetQueuedCompletionStatus
或NtRemoveIoCompletion
来提取此数据包。这不是“民意调查”。从另一方面,如果您使用ApcRoutine
系统插入apc到您的线程。使用两种方式同时进行通知和并发 - 这是逻辑错误,并且在这种情况下返回给你STATUS_INVALID_PARAMETER
时内核正确。
遗憾的是,这在MSDN中并未明确说明,或者我最不能说明这一点。但是一些研究+ WRK源代码有助于理解这种情况:
WSARecv
在内部调用ZwDeviceIoControlFile
,并在内核中调用IopXxxControlFile
。当lpCompletionRoutine != 0
ApcRoutine
的{{1}}参数出现且您在this点完全失败时:
IopXxxControlFile
你也可以在堆栈中分配m_wsabuf。有效必须只是缓冲区 //
// 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;
}
如果此功能以重叠方式完成,则为 Winsock服务提供商有责任捕获 WSABUF 从此调用返回之前的结构。这使应用程序 构建 lpBuffers 指向的基于堆栈的 WSABUF 数组 参数。
因此,您只能将WSABUF
缓冲区放置到OVERLAPPED_EX
但不是整个WSABUF.buf
的缓冲区。但这与您的错误无关