Jest to ogólny problem z IOCP, wykonujesz wywołanie niskiego poziomu do stosu sterowników TCP/IP. Które, jak wszystkie sterowniki w systemie Windows, zgłosić awarię z kodami błędów NTSTATUS. Oczekiwany błąd tutaj to STATUS_CONNECTION_RESET.
Te natywne kody błędów należy przetłumaczyć na kod błędu winapi. To tłumaczenie jest zwykle kontekstowe, to zależy od tego, co biblioteka Winapi wydała polecenie sterownika. Innymi słowy, możesz odzyskać tylko błąd WSAECONNRESET, jeśli jest to biblioteka Winsock, która wykonała tłumaczenie. Ale tak się nie stało w twoim programie, to GetQueuedCompletionStatus() obsługiwał ten błąd.
Jest to ogólna funkcja pomocnicza, która obsługuje IOCP dla dowolnego sterownika urządzenia. Nie ma kontekstu, struktura OVERLAPPED nie jest wystarczająca, aby wskazać, w jaki sposób rozpoczęło się żądanie wejścia/wyjścia. Przejdź na numer this KB article, który dokumentuje domyślne mapowanie z kodów błędów NTSTATUS na kody błędów winapi. Mapowanie używane przez GetQueuedCompletionStatus(). Istotne pozycje na liście to:
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
To były, hmm, nie fantastyczne wybory. Prawdopodobnie powraca do bardzo wczesnego systemu Windows, kiedy Lanman był preferowaną warstwą sieci. WSAGetLastError() jest dość bezsilny, aby odwzorować ERROR_NETNAME_DELETED z powrotem na błąd specyficzny dla WSA, kod NTSTATUS został utracony, gdy GetQueuedCompletionStatus() ustawił "ostatni błąd" kodu dla wątku. A więc nie, po prostu zwraca to, co może.
Czego bym oczekiwać jest funkcją WSAGetQueuedCompletionStatus(), więc to tłumaczenie błąd może zdarzyć się prawidłowo, stosując zasady Winsock. Nie ma ani jednego. W dzisiejszych czasach wolę używać najwyższych uprawnień do prawidłowego pisania kodu Windows, źródła .NET Framework dostępnego z poziomu Reference Source. Połączyłem ze źródłem dla metody SocketAsyncEventArgs.CompletionCallback(). Który zawiera klucz:
// 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();
Lub innymi słowy, trzeba dokonać dodatkowy wezwanie do WSAGetOverlappedResult(), aby uzyskać prawidłowe wartości zwracanej od GetLastError().To nie jest zbyt intuicyjne :)
64 to 'ERROR_NETNAME_DELETED'. Tak po prostu działa. Będziesz musiał sobie z tym poradzić. Z mojego doświadczenia wynikającego z używania WinSock IOCP zazwyczaj wynika, że "ERROR_NETNAME_DELETED", a nie "WSAECONNRESET", więc po prostu obsługuj oba błędy tak, jakby były takie same. –
@Remy Lebeau Czy wszystkie kody błędów zwrócone przez 'WSAGetLastError()' wewnątrz wątku IOCP będą 'ERROR_NETNAME_DELETED' lub po prostu' WSAECONNRESET'? – Tom
Nie wszystkie błędy, ale nie wiem, które konkretnie są mapowane na 'ERROR_NETNAME_DELETED'. Zawsze traktuję to jako nieoczekiwany rozłączenie. Czy to naprawdę ma znaczenie, jeśli zostało spowodowane przez "RST", czy nie? Nie jest to pełne wdzięku rozłączenie w jedną lub w drugą stronę. –