2015-09-04 13 views
6

wierzę sekwencji wyłączania się następująco (w sposób opisany here)zamykania gniazda: kiedy należy skorzystać SocketShutdown.Both

Socket Shutdown sequence

MSDN documentation (rozdział Uwagi) brzmi:

Podczas korzystania z połączenia zorientowanego na połączenie Socket, zawsze wywołaj metodę Shutdown przed zamknięciem Socket. Zapewnia to, że wszystkie dane są wysyłane i odbierane na podłączonym gnieździe przed jego zamknięciem.

To zdaje się sugerować, że jeśli używam Shutdown(SocketShutdown.Both), wszelkie dane, które nie zostały jeszcze odebrane, może nadal być spożywane. Aby przetestować to:

  • Ciągle przesyłam dane do klienta (poprzez Send w osobnym wątku).
  • Klient wykonał Shutdown(SocketShutdown.Both).
  • Oddzwanianie na serwerze BeginReceive powoduje jednak, że zgłasza wyjątek: Istniejące połączenie zostało przymusowo zamknięte przez zdalny host. Oznacza to, że nie mogę odebrać wartości zwracanej 0, a następnie wywołać Shutdown.

Zgodnie z życzeniem opublikowałem poniższy kod po stronie serwera (jest zawinięty w formularz Windows i został utworzony jako eksperyment). W moim scenariuszu testowym nie widziałem stanu CLOSE_WAIT stanu w TCPView jak zwykle, bez wysyłania ciągłych danych. Potencjalnie zrobiłem coś złego i niepoprawnie przerywa mi konsekwencje. W innym eksperymencie:

  • Klient łączy się z serwerem.
  • Klient wykonuje Shutdown(SocketShutdown.Both).
  • Serwer otrzymuje potwierdzenie zamknięcia i wysyła niektóre dane w odpowiedzi. Serwer wykonuje również Shutdown.
  • Klient otrzymuje dane z serwera, ale obok BeginReceive nie jest dozwolone: ​​prośbą, aby wysyłać i odbierać dane było zabronione, ponieważ gniazdo już zostały zamknięte w tym kierunku z poprzedniego wywołania shutdown

W w tym scenariuszu nadal oczekiwałem wartości zwracanej z 0 z EndReceive do Close gniazda. Czy to oznacza, że ​​powinienem zamiast tego użyć Shutdown(SocketShutdown.Send)? Jeśli tak, to kiedy należy użyć Shutdown(SocketShutdown.Both)?

Kod z pierwszego eksperymentu:

private TcpListener SocketListener { get; set; } 
private Socket ConnectedClient { get; set; } 
private bool serverShutdownRequested; 
private object shutdownLock = new object(); 

private struct SocketState 
{ 
    public Socket socket; 
    public byte[] bytes; 
} 

private void ProcessIncoming(IAsyncResult ar) 
{ 
    var state = (SocketState)ar.AsyncState; 
    // Exception thrown here when client executes Shutdown: 
    var dataRead = state.socket.EndReceive(ar); 
    if (dataRead > 0) 
    { 
    state.socket.BeginReceive(state.bytes, 0, state.bytes.Length, SocketFlags.None, ProcessIncoming, state); 
    } 
    else 
    { 
    lock (shutdownLock) 
    { 
     serverShutdownRequested = true; 
     state.socket.Shutdown(SocketShutdown.Both); 
     state.socket.Close(); 
     state.socket.Dispose(); 
    } 
    } 
} 

private void Spam() 
{ 
    int i = 0; 
    while (true) 
    { 
    lock (shutdownLock) 
    { 
     if (!serverShutdownRequested) 
     { 
     try { ConnectedClient.Send(Encoding.Default.GetBytes(i.ToString())); } 
     catch { break; } 
     ++i; 
     } 
     else { break; } 
    } 
    } 
} 

private void Listen() 
{ 
    while (true) 
    { 
    ConnectedClient = SocketListener.AcceptSocket(); 
    var data = new SocketState(); 
    data.bytes = new byte[1024]; 
    data.socket = ConnectedClient; 
    ConnectedClient.BeginReceive(data.bytes, 0, data.bytes.Length, SocketFlags.None, ProcessIncoming, data); 
    serverShutdownRequested = false; 
    new Thread(Spam).Start(); 
    } 
} 

public ServerForm() 
{ 
    InitializeComponent(); 
    var hostEntry = Dns.GetHostEntry("localhost"); 
    var endPoint = new IPEndPoint(hostEntry.AddressList[0], 11000); 
    SocketListener = new TcpListener(endPoint); 
    SocketListener.Start(); 
    new Thread(Listen).Start(); 
} 
+0

'Wyślij' zwraca także' 0' podczas sekwencji wyłączania. Jeśli zaktualizuję * serwer *, aby to uwzględnić, zanim moja flaga zostanie ustawiona w metodzie 'Spam',' EndReceive' nadal zgłasza wyjątek. Podejrzewam, że to wszystko dzieje się, ponieważ stan gniazda 'CLOSE_WAIT' został w jakiś sposób pominięty. – Pooven

Odpowiedz

1

To zdaje się sugerować, że jeśli używam wyłączanie (SocketShutdown.Both) wszelkie dane, które nie zostały jeszcze odebrane, może nadal być spożywane

Nie, te dane zniknęły. Wyraźnie oczyściłeś go, wyłączając.Nie ma sposobu, aby wiedzieć, ile danych będzie wysyłanych przez stronę zdalną, nawet jeśli zamknięcie nie oczyści wyniku, byłoby niedeterministyczne i nic by ci nie pomogło.

Istniejące połączenie zostało gwałtownie zamknięte przez zdalnego hosta

To nie powinno się zdarzyć. Kod pocztowy.

Czy to znaczy, że zamiast tego powinienem użyć Shutdown (SocketShutdown.Send)?

Jeśli chcesz nadal otrzymywać informacje, dlaczego zamknąłeś otrzymywanie? Po prostu tego nie rób. Wyłącz tylko wysyłanie.

Jeśli tak, to kiedy należy użyć funkcji Shutdown (SocketShutdown.Both)?

Kiedy nie chcesz otrzymywać ani wysyłać. Czy możesz wyjaśnić, dlaczego nie jest to dla ciebie oczywiste? Może jest nieporozumienie lub nie dostaję pytania.

+0

Dziękuję za odpowiedź. ** 1. ** Jeśli dane nie istnieją, to w jaki sposób różnią się 'Zamknij' i' Zamknij'? Co oznacza dokumentacja: "Zapewnia to, że wszystkie dane są wysyłane i odbierane"? ** 2. ** Wysłałem tylko kod serwera; czy to wystarczy? ** 3. ** Czy "Shutdown (SocketShutdown.Send)" nie oznacza, że ​​zezwalam na przychodzące dane? ** 4. ** Jeśli nie chcę ani odbierać, ani wysyłać, to po co muszę "Zamykać" zamiast "Zamknij"? Powiązany z pozycją 1, staram się zrozumieć poprawną sekwencję rozłączenia gniazd i konsekwencje jej nie wykonania. – Pooven

+0

1. Zamknij nie zgłasza błędów. Zwykle powinieneś wywołać zamknięcie przed zamknięciem, aby zgłaszać błędy. 2. Potrzebuję też klienta. Serwer wygląda dobrze. 3. Tak. 4. Patrz (1) .; Poprawna sekwencja to zamknięcie (obie); dysponować. Nigdy nie dzwonuj blisko. – usr

Powiązane problemy