我致电WSARecv()
并返回WSA_IO_PENDING
。然后我从另一端发送了一个RST
数据包。另一个线程中存在的GetQueuedCompletionStatus()
函数已按预期返回FALSE
,但当我调用WSAGetLastError()
时,我得到了64
而不是WSAECONNRESET
。
为什么WSAGetLastError()
没有返回WSAECONNRESET
?
修改:
我忘了提及当我在失败的WSAGetLastError()
之后直接调用WSARecv()
时(因为收到了RST
数据包),返回的错误代码是WSAECONNRESET
并且不是64
。
所以看起来返回的错误代码取决于WSARecv()
在调用后是否直接失败,或者在检索完成数据包后是否失败。
答案 0 :(得分:13)
这是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()获取正确的返回值。这不是很直观:)