2015-03-08 8 views
8

Zadzwoniłem pod numer WSARecv(), który zwrócił WSA_IO_PENDING. Wysłałem następnie z drugiego końca pakiet RST. Funkcja GetQueuedCompletionStatus(), która istnieje w innym wątku, została zwrócona FALSE zgodnie z oczekiwaniami, ale kiedy zadzwoniłem pod numer WSAGetLastError(), otrzymałem 64 zamiast WSAECONNRESET.Wywołanie WSAGetLastError() z wątku IOCP zwraca niepoprawny wynik

Dlaczego więc WSAGetLastError() nie wrócił WSAECONNRESET?


Edit:

Zapomniałam wspomnieć, że gdy zgłoszę WSAGetLastError() bezpośrednio po braku WSARecv() (wskutek RST pakiet odbierany), kod błędu zwrócony jest WSAECONNRESET i nie 64.

Wygląda na to, że kod błędu został zwrócony w zależności od tego, czy WSARecv() zawiodła bezpośrednio po wywołaniu, czy też zawiodła później podczas pobierania pakietu uzupełniającego.

+1

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. –

+0

@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

+0

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ę. –

Odpowiedz

12

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 :)

Powiązane problemy