2014-05-09 6 views
6

Poniższy kod, pomimo pozornie zamknięcia gniazda UDP, pozostawia go zawieszony i nie może ponownie połączyć się z tym samym adresem/portem.(Najwyraźniej) Wdzięcznie zamknięty UDPClient opuszcza gniazdo zablokowane

Są zmienne klasy używam:

Thread t_listener; 
    List<string> XSensAvailablePorts; 
    private volatile bool stopT_listener = false;   
    volatile UdpClient listener; 
    IPEndPoint groupEP; 

tworzę i rozpoczęcie nowego wątku z metody, która będzie obsługiwać połączenia gnieździe i słuchanie:

private void startSocketButton_Click(object sender, RoutedEventArgs e) 
     { 
      stopT_listener = false; 
      closeSocketButton.IsEnabled = true; 
      startSocketButton.IsEnabled = false; 
      t_listener = new Thread(UDPListener); 
      t_listener.Name = "UDPListenerThread"; 
      t_listener.Start(); 
     } 

Metoda jest następująca (Używam timeOut na Receive, aby nie zostawiać go zablokowanego, jeśli nic nie jest wysyłane na gniazdo i nie jest wydawane Stop):

private void UDPListener() 
    { 
     int numberOfPorts = XSensAvailablePorts.Count(); 
     int currAttempt = 0; 
     int currPort = 0; 
     bool successfullAttempt = false; 
     while (!successfullAttempt && currAttempt < numberOfPorts) 
     { 
      try 
      { 
       currPort = Int32.Parse(XSensAvailablePorts[currAttempt]); 
       listener = new UdpClient(currPort); 
       successfullAttempt = true; 
      } 
      catch (Exception e) 
      { 
       currAttempt++; 
      } 
     } 
     if (successfullAttempt) 
     { 
      //timeout = 2000 millis 
      listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 2000); 
      //Tried with and without, no change: listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
      statusTB.Dispatcher.BeginInvoke((Action)delegate() { statusTB.Text = "Connected on port:" + currPort; }); 
      groupEP = new IPEndPoint(IPAddress.Parse("143.225.85.130"), currPort); 

      byte[] receive_byte_array; 
      try 
      { 
       while (!stopT_listener) 
       { 
        try 
        { 
         receive_byte_array = listener.Receive(ref groupEP); 
         if (receive_byte_array.Length == 0 || receive_byte_array == null) 
          continue; 

         ParseData(receive_byte_array, samplingDatagramCounter); 

        } 
        catch (SocketException ex) 
        { 
         if (ex.SocketErrorCode == SocketError.TimedOut) 
          continue; 
        } 


       } 
      } 
      catch (Exception e) 
      { 
       Debug.Print(e.Message); 
      } 
      finally 
      { 
       if (listener != null) 
       { 
        listener.Client.Shutdown(SocketShutdown.Both); 
        listener.Close(); 
       } 
      } 
     } 
     statusTB.Dispatcher.BeginInvoke((Action)delegate() { statusTB.Text = "Not Connected"; }); 
     return; 
    } 

zamówić gwintu/gniazda, aby zatrzymać przy użyciu tej metody:

private void closeSocketButton_Click(object sender, RoutedEventArgs e) 
     { 
      stopT_listener = true; 
      closeSocketButton.IsEnabled = false; 
      startSocketButton.IsEnabled = true; 
      t_listener.Join(); 
      if (listener.Client != null) 
      { 
       listener.Client.Shutdown(SocketShutdown.Both); 
       listener.Close(); 
      } 
      if (t_listener.IsAlive) 
      { 
       t_listener.Abort(); 
       statusTB.Text = "Aborted"; 
      } 
      else 
       statusTB.Text = "Not Connected"; 
     } 

Pomimo sprawdzania w debugowania, że ​​gniazdo zostało zamknięte, gdybym ponownie podłączyć do tego samego portu, jestem w stanie to zrobić, ponieważ to podnosi wyjątek SocketException, który mówi, że dozwolone jest tylko jedno użycie portu/adresu.

+0

Czy to możliwe, czym jest 'listener.Client' po' null' po kliknięciu przycisku? Nie wywołasz wtedy 'listener.Close()'. – Sinatr

+0

@Sinatr Sprawdziłem debugowanie i tak nie jest. –

+0

. Zamknij() powinno całkowicie pozbyć się obiektu. musi to być coś innego, tak jak nie właściwie zamknięcie gniazda (prawdopodobnie z powodu jakiegoś wyjątku, który nie jest obsługiwany i po prostu pomijany). Nie wiem, ponieważ właśnie użyłem metody asynchronicznej .BeginReceive do obsługi danych. ; p – porkchop

Odpowiedz

0

Umieszczam podany przez Ciebie kod w prostym formularzu, aby go uruchomić i ... Nie mogę bezpośrednio odtworzyć Twojego problemu. Nie wysyłałem żadnych danych do klienta, ale z tego co rozumiem, nie powinno to nic zmieniać, ponieważ jest to UDP, a my badamy (ponownie) otwierając gniazdo, nie przesyłając danych.

Po kliknięciu przycisków Start/Stop gniazdo jest zawsze prawidłowo otwierane i zamykane, ponowne otwieranie działa zgodnie z przeznaczeniem. Dla mnie to jedyny sposób, aby wymusić SocketException wzmiance było wprowadzenie jakiegoś oczywistego nadużycia logiki Gniazdo:

  1. Uruchom dwie instancje aplikacji i kliknij przycisk Start w obu.
  2. Usunąć OBUWANIE wystąpień zamykania i zamykania (zatrzymanie nie powoduje zamknięcia gniazda).
  3. Uruchamianie aplikacji, otwieranie gniazda, zamykanie aplikacji bez zamykania gniazda, ponowne uruchomienie aplikacji, próba otwarcia gniazda.

Jedyne zmiany wprowadzone w kodzie to usunięcie linii ParseData (...) i dodanie niektórych portów do listy XSensAvailablePorts.

Czy możesz sprawdzić, czy port jest nadal otwarty po tym, jak go najwyraźniej zamknąłeś? Możesz użyć netstat -an lub doskonałego narzędzia ProcessExplorer. Możesz również sprawdzić, czy wątek t_listener kończy się poprawnie (standardowy Menedżer zadań lub ProcessExplorer może ci pomóc).

+1

Nie mam dostępu do HW/SW, z którym się komunikowałem (jak być może zrozumiałeś z nazwy zmiennych, do których uzyskuję dostęp do strumienia gniazda XSens body suit do śledzenia ciała), ale sprawdzę to w przyszłym tygodniu . Miałem tak wiele problemów ze strumieniem danych tego komponentu, że podejrzewam, że może to być wina serwera, coś się szaleje. –

0

Ustaw obiekt detektora na NULL, aby zwolnić zasób, który powinien również zwolnić połączenie.

+0

Zamiast ustawiać obiekt na wartość null i czekać, aż GC zwolni zasoby, wymusiłem zwolnienie zasobów przy użyciu metody Close()/Dispose(), ale też nie działało. –

0

Mam ten sam problem, a problem jest w UDPClient.Receive(), ona utrzymuje gniazdo w stanie używanym nawet ciebie zadzwonić Close/shutdown/...`

try{ // receive loop} 
catch{} 
finally { 
UDP_Listner.Close(); 
        UDP_Listner = null; 
       } 

EDIT:

t_listener = new Thread(UDPListener);//replace by : 
t_listener = new Thread(new ThreadStart(UDPListener)); 

`

bezpiecznie blisko gniazdo & gwint (http://msdn.microsoft.com/en-us/library/system.threading.threadstart(v=vs.110).aspx)

0

Mam ten sam problem, jestem najbezpieczniejszym programista, zawsze blisko wszystko ładnie. jednak znalazłem, że klasa .net nie zamyka wystarczająco szybko gniazda. ponieważ jeśli pójdę powoli, to się nie stanie, ale jeśli otworzę i zamknę (wyczyści się całkowicie) i otworzę szybko, otrzymam ten sam błąd. zwłaszcza jeśli użytkownik chce ponownie uruchomić ten sam kod i ponownie otworzyć port.

+0

Czy to jest odpowiedź czy komentarz? – heltonbiker

0

Może to być stara odpowiedź, ale przy próbie znalezienia użytecznego portu, ale który się nie powiódł, usunąłbym instancję odsłuchiwania testowaną przed następną iteracją.

  try 
      { 
       currPort = Int32.Parse(XSensAvailablePorts[currAttempt]); 
       listener = new UdpClient(currPort); 
       successfullAttempt = true; 
      } 
      catch (Exception e) 
      { 
       currAttempt++; 

       if(listener != null) 
       { 
        listener.Close(); 
       } 
      } 
Powiązane problemy