【问题标题】:Calling WSAGetLastError() from an IOCP thread return incorrect result从 IOCP 线程调用 WSAGetLastError() 返回不正确的结果
【发布时间】:2015-05-09 14:32:03
【问题描述】:

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

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


编辑:

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

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

【问题讨论】:

  • 64 是ERROR_NETNAME_DELETED。这就是它的工作方式。你将不得不处理它。根据我使用 WinSock IOCP 的经验,它通常会报告 ERROR_NETNAME_DELETED 而不是 WSAECONNRESET,因此只需将这两个错误视为相同即可。
  • @Remy Lebeau 在 IOCP 线程中 WSAGetLastError() 返回的所有错误代码是 ERROR_NETNAME_DELETED 还是只是 WSAECONNRESET
  • 并非所有错误,但我不知道哪些错误具体映射到ERROR_NETNAME_DELETED。我总是将其视为意外的断开连接。它是否由RST 引起真的很重要吗?这不是一种优雅的断开方式。
  • @Remy Lebeau 在我的情况下这并不重要,但在其他情况下知道断开连接的原因可能很重要。

标签: c++ sockets iocp


【解决方案1】:

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

这些原生错误码需要翻译成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 代码的终极权威,即来自 Reference Source 的 .NET Framework 源代码。我链接到 SocketAsyncEventArgs.CompletionCallback() 方法的源代码。其中包含密钥:

// 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() 获得正确的返回值。这不是很直观:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-11
    • 2016-02-06
    • 2017-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多