2015-03-08 3 views
8

Я позвонил WSARecv(), который вернул WSA_IO_PENDING. Затем я отправил пакет RST с другого конца. Функция GetQueuedCompletionStatus(), которая существует в другом потоке, вернула FALSE, как и ожидалось, но когда я позвонил WSAGetLastError(), я получил 64 вместо WSAECONNRESET.Вызов WSAGetLastError() из потока IOCP возвращает неверный результат

Так почему же WSAGetLastError() не вернулся WSAECONNRESET?


Edit:

Я забыл упомянуть, что, когда я называю WSAGetLastError() сразу после провала попытки WSARecv() (из-за RST пакет принимается), то возвращается код ошибки WSAECONNRESET и не 64.

Похоже, что возвращенный код ошибки зависит от того, произошел ли отказ WSARecv() после его вызова или позже был неудачен при получении пакета завершения.

+1

64 is 'ERROR_NETNAME_DELETED'. Так оно и работает. Вам придется с этим справиться. По моему опыту с использованием WinSock IOCP он обычно сообщает 'ERROR_NETNAME_DELETED', а не' WSAECONNRESET', поэтому просто обрабатывайте обе ошибки, как если бы они были одинаковыми. –

+0

@Remy Lebeau Все коды ошибок, возвращаемые 'WSAGetLastError()' внутри потока IOCP, будут 'ERROR_NETNAME_DELETED' или просто' WSAECONNRESET'? – Tom

+0

Не все ошибки, но я не знаю, какие именно они относятся к 'ERROR_NETNAME_DELETED'. Я всегда отношусь к нему как к неожиданному отключению. Действительно ли это имеет значение, если оно было вызвано «RST» или нет? Это не изящное отключение так или иначе. –

ответ

12

Это общая проблема с IOCP, вы вызываете низкоуровневый вызов стека драйверов TCP/IP. Который, как и все драйверы в Windows, сообщает об ошибке с ошибками NTSTATUS. Ожидаемая ошибка здесь: STATUS_CONNECTION_RESET.

Эти исходные коды ошибок должны быть переведены на код ошибки winapi. Этот перевод обычно контекстно-зависимый, это зависит от того, какая библиотека winapi выдала команду драйвера. Другими словами, вы можете получить ошибку WSAECONNRESET, только если это была библиотека Winsock, которая сделала перевод. Но это не то, что произошло в вашей программе, именно GetQueuedCompletionStatus() обработал ошибку.

Эта общая вспомогательная функция, которая обрабатывает IOCP для любого драйвера устройства. Нет никакого контекста, структура OVERLAPPED не достаточно, чтобы указать, как начался запрос ввода-вывода. Повернуть на 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, код NTSTATUS был потерян, когда GetQueuedCompletionStatus() установил код последней ошибки для потока. Таким образом, это не так, просто возвращает то, что может.


Что вы бы ожидать функция WSAGetQueuedCompletionStatus(), поэтому этот перевод ошибка может произойти правильно, используя правила Winsock. Нет. В эти дни я предпочитаю использовать максимальную власть над тем, как правильно писать код Windows, источник .NET Framework, доступный с Reference Source. Я связан с источником для метода 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().Это не очень интуитивно понятно :)

Смежные вопросы