c++ - 从 IOCP 线程调用 WSAGetLastError() 返回不正确的结果

标签 c++ sockets iocp

我调用了 WSARecv(),它返回了 WSA_IO_PENDING。然后我从另一端发送了一个 RST 数据包。存在于另一个线程中的 GetQueuedCompletionStatus() 函数按预期返回了 FALSE,但是当我调用 WSAGetLastError() 时,我得到了 64 而不是 WSAECONNRESET

那么为什么 WSAGetLastError() 没有返回 WSAECONNRESET


编辑:

我忘了提到当我在 WSARecv() 失败后直接调用 WSAGetLastError() 时(因为 RST 数据包被接收),返回的错误代码是 WSAECONNRESET 而不是 64

所以看起来返回的错误代码取决于 WSARecv() 是在调用它之后直接失败,还是稍后在检索完成数据包时失败。

最佳答案

这是 IOCP 的一般问题,您正在对 TCP/IP 驱动程序堆栈进行低级调用。与 Windows 中的所有驱动程序一样,它会报告带有 NTSTATUS 错误代码的故障。此处的预期错误是 STATUS_CONNECTION_RESET。

这些 native 错误代码需要转换为 winapi 错误代码。此翻译通常上下文相关,它取决于发出驱动程序命令的 winapi 库。换句话说,如果是 Winsock 库进行了翻译,您只能返回 WSAECONNRESET 错误。但这不是您的程序中发生的情况,是 GetQueuedCompletionStatus() 处理了错误。

这是一个通用的辅助函数,可以处理任何设备驱动程序的 IOCP。没有上下文,OVERLAPPED 结构不足以表明 I/O 请求是如何开始的。转this KB article ,它记录了从 NTSTATUS 错误代码到 winapi 错误代码的默认映射。 GetQueuedCompletionStatus() 使用的映射。列表中的相关条目是:

STATUS_NETWORK_NAME_DELETED          ERROR_NETNAME_DELETED
STATUS_LOCAL_DISCONNECT              ERROR_NETNAME_DELETED
STATUS_REMOTE_DISCONNECT             ERROR_NETNAME_DELETED
STATUS_ADDRESS_CLOSED                ERROR_NETNAME_DELETED
STATUS_CONNECTION_DISCONNECTED       ERROR_NETNAME_DELETED
STATUS_CONNECTION_RESET              ERROR_NETNAME_DELETED 

咳咳,这些并不是很好的选择。可能可以追溯到非常早期的 Windows,当时 Lanman 是首选的网络层。 WSAGetLastError() 无法将 ERROR_NETNAME_DELETED 映射回 WSA 特定错误,当 GetQueuedCompletionStatus() 为线程设置“最后一个错误”代码时,NTSTATUS 代码丢失。所以它不会,它只是返回它可以返回的内容。


期望的是一个 WSAGetQueuedCompletionStatus() 函数,因此可以使用 Winsock 规则正确地进行错误转换。没有一个。这些天我更喜欢使用关于如何正确编写 Windows 代码的最终权威,.NET Framework 源代码可从 Reference Source 获得。 .我链接到 SocketAsyncEventArgs.CompletionCallback() 方法的源代码。其中包含 key :

// The Async IO completed with a failure.
// here we need to call WSAGetOverlappedResult() just so Marshal.GetLastWin32Error() will return the correct error.
bool success = UnsafeNclNativeMethods.OSSOCK.WSAGetOverlappedResult(
    m_CurrentSocket.SafeHandle,
    m_PtrNativeOverlapped,
    out numBytes,
    false,
    out socketFlags);
socketError = (SocketError)Marshal.GetLastWin32Error();

或者换句话说,您必须额外调用 WSAGetOverlappedResult() 才能从 GetLastError() 获得正确的返回值。这不是很直观:)

关于c++ - 从 IOCP 线程调用 WSAGetLastError() 返回不正确的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28925003/

相关文章:

c++ - 了解 C++ 中的删除

c - recv() 和 accept() 可以互换吗?

java - 将数据从 C 服务器代码发送到 java 客户端代码

winapi - CreateFile() 返回 INVALID_HANDLE_VALUE 但 GetLastError() 是 ERROR_SUCCESS

c++ - 关于windows iocp的一个问题

c++ - 在 C++ 上获取本地计算机的 IP 地址

c++ - 遍历二维数组的所有子数组

c++ - 如何使用模板函数和 CUDA

sockets - Nodejs 流式传输

c++ - WSARecv 一次可以接收的最大字节数是多少?