2010-09-18 8 views
6

Używam netNamedPipeBinding do wykonywania między-procesowej komunikacji WCF z aplikacji Windows do usługi Windows.Wyjątek WCF odebrany przy zamkniętym połączeniu z oddzwonionymi w użyciu

Teraz moja aplikacja działa dobrze na wszystkich innych kontach (po walce z moim uczciwym udziałem wyjątków WCF, jak każdy, kto pracował z WCF będzie wiedział ..), ale ten błąd jest taki, który okazuje się być dość odporny.

Aby namalować obraz z mojego scenariusza: moja usługa Windows może być ustawiona w kolejce do wykonania jakiejś pracy w dowolnym momencie za pomocą przycisku naciśniętego w aplikacji Windows, a następnie rozmawia przez netNamedPipeBinding, która jest wiązaniem obsługującym wywołania zwrotne (dwie komunikacji), jeśli nie jesteś zaznajomiony i inicjuje żądanie wykonania tej pracy, (w tym przypadku procedura przesyłania pliku), powoduje ona również wywoływanie zwrotne (zdarzenia) co kilka sekund, począwszy od postępu pliku, szybkości transferu itd. itd. do aplikacji Windows, więc istnieje dość ciasna integracja klient-serwer; w ten sposób otrzymuję moje postępy w działaniu mojej usługi Windows z powrotem do mojej aplikacji Windows.

Teraz wszystko jest wspaniałe, bogowie WCF są teraz ze mną stosunkowo szczęśliwi, oprócz jednego paskudnego wyjątku, który otrzymuję za każdym razem, gdy zamykam aplikację przedwcześnie (co jest doskonale uzasadnionym scenariuszem). Podczas gdy transfer jest w toku, a callbacks strzelają dość ciężko, otrzymuję ten błąd:

System.ServiceModel.ProtocolException: 
    The channel received an unexpected input message with Action 
    'http://tempuri.org/ITransferServiceContract/TransferSpeedChangedCallback' 
    while closing. You should only close your channel when you are not expecting 
    any more input messages. 

Teraz rozumiem, że błąd, ale niestety nie mogę zagwarantować, aby zamknąć mój kanał po nie otrzymaniu żadnych więcej wejściowych Komunikat ten, jako użytkownik może zamknąć aplikację w dowolnym momencie, dlatego też praca będzie kontynuowana w tle usługi Windows (coś w rodzaju działania skanera antywirusowego). Użytkownik powinien mieć możliwość uruchamiania i zamykania aplikacji narzędzi do zarządzania wygraną tak, jak im się podoba bez zakłóceń.

Teraz błąd, otrzymuję natychmiast po wykonaniu mojego połączenia Unsubscribe(), które jest drugim ostatnim połączeniem przed zakończeniem aplikacji i co uważam za preferowany sposób rozłączenia klienta WCF. Całe wypisanie się przed zamknięciem połączenia powoduje po prostu usunięcie identyfikatora klienta z tablicy, która była przechowywana lokalnie w usłudze winf usługi wcf (ponieważ jest to WSPÓLNE udostępnienie zarówno przez usługę wygrywania, jak i aplikację Windows, ponieważ usługa wygrywająca może wykonywać pracę w zaplanowane zdarzenia samodzielnie) i po usunięciu tablicy identyfikacyjnej klienta, mam nadzieję, że to powinno być czyste odłączenie.

W wyniku tego, oprócz otrzymania wyjątku, moja aplikacja zawiesza się, interfejs użytkownika jest w całkowitym zamknięciu, paski postępu i wszystko w środku, z wszystkimi znakami wskazującymi na stan wyścigu lub impas WCF [westchnienie], ale jestem teraz bardzo doświadczony w tej dziedzinie i uważam, że jest to sytuacja relatywnie odizolowana i przeczytanie wyjątku takim, jakie jest, nie wydaje mi się, że jest to kwestia "wątku" per se, ponieważ mówi bardziej o kwestii wczesnego odłączenia, które następnie spirale wszystkie moje wątki w chaos, być może powodując blokadę.

Moja Unsubscribe() podejście na klienta wygląda następująco:

public void Unsubscribe() 
    { 
     try 
     { 
      // Close existing connections 
      if (channel != null && 
       channel.State == CommunicationState.Opened) 
      { 
       proxy.Unsubscribe(); 
      } 
     } 
     catch (Exception) 
     { 
      // This is where we receive the 'System.ServiceModel.ProtocolException'. 
     } 
     finally 
     { 
      Dispose(); 
     } 
    } 

i mój Dispose() metodę, która powinna wykonać czystą odłączyć:

public void Dispose() 
    { 
     // Dispose object 
     if (channel != null) 
     { 
      try 
      { 
       // Close existing connections 
       Close(); 
       // Attempt dispose object 
       ((IDisposable)channel).Dispose(); 
      } 
      catch (CommunicationException) 
      { 
       channel.Abort(); 
      } 
      catch (TimeoutException) 
      { 
       channel.Abort(); 
      } 
      catch (Exception) 
      { 
       channel.Abort(); 
       throw; 
      } 
     } 
    } 

a usługa WCF Subscription() odpowiednika i klasę atrybuty (dla odniesienia) w usłudze Windows serwer (tutaj nie ma nic trudnego, a mój wyjątek występuje po stronie klienta):

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, 
    ConcurrencyMode = ConcurrencyMode.Multiple)] 
    public class TransferService : LoggableBase, ITransferServiceContract 
    { 
     public void Unsubscribe() 
     { 
      if (clients.ContainsKey(clientName)) 
      { 
       lock (syncObj) 
       { 
        clients.Remove(clientName); 
       } 
      } 

#if DEBUG 
      Console.WriteLine(" + {0} disconnected.", clientName); 
#endif 
     } 
     ... 
    } 

Interfejs:

[ServiceContract(
    CallbackContract = typeof(ITransferServiceCallbackContract), 
    SessionMode = SessionMode.Required)] 
public interface ITransferServiceContract 
{ 
    [OperationContract(IsInitiating = true)] 
    bool Subscribe(); 

    [OperationContract(IsOneWay = true)] 
    void Unsubscribe(); 
    ... 
} 

interfejs umowy zwrotnej, to nie ma nic bardzo ekscytujące zrobić, po prostu wywołuje zdarzenia za pośrednictwem delegatów itd. Powodem tego jest włączone, aby pokazać ty moje atrybuty. Zrobiłem łagodzić jeden zestaw zakleszczenia już o tym UseSynchronizationContext = false:

[CallbackBehavior(UseSynchronizationContext = false, 
ConcurrencyMode = ConcurrencyMode.Multiple)] 
public class TransferServiceCallback : ITransferServiceCallbackContract 
{ ... } 

naprawdę nadzieję, że ktoś może mi pomóc! Wielkie dzięki = :)

+0

nie wiem na temat konkretnego problemu, ale dla informacji snafu gwintowania brzmi * ewentualnie * ze względu na to, jak WCF używa Sync kontekstu (który jest za pośrednictwem formularza w WinForm etc). –

+0

Dziękuję Marc, tak, że mnie dopadłeś i złagodziłem jeden zestaw zakleszczeń czytając o tym problemie, sztuczka polegała na ustawieniu 'UseSynchronizationContext = false' na umowie oddzwaniania;) Dodam to do moich przykładów. – GONeale

+0

ah, z prawej; dobrze widzieć, że już to zakryłeś; p –

Odpowiedz

11

O mój Boże, znalazłem problem.

Ten wyjątek nie miał nic wspólnego z zawieszeniem aplikacji, to był wyjątek ostrożnościowy, który można bezpiecznie złapać.

Nie uwierzyłbyś, spędziłem około 6 godzin na tym błędzie, okazało się, że jest to channel.Close() blokujące oczekiwanie na oczekujące żądania WCF do ukończenia (które nigdy by się nie skończyły, dopóki transfer się nie skończy! pokonuje cel!)

Po prostu przeszedłem linię łamania brutalnej siły po linii, mój problem polegał na tym, że byłem zbyt wolny ..... nigdy się nie zawiesił, ponieważ jakoś kanał byłby dostępny do zamknięcia (nawet przed transfer się skończył), więc musiałem przełamać F5, a następnie szybko zrobić krok, by złapać zawieszkę, i to jest ta linia, na której skończyła. Teraz po prostu stosuję wartość limitu czasu do operacji Close() i łapię ją za pomocą TimeoutException, a następnie mocno przerywam kanał, jeśli nie można go zamknąć w odpowiednim czasie!

Zobacz kod poprawek:

private void Close() 
{ 
    if (channel != null && 
     channel.State == CommunicationState.Opened) 
    { 
     // If cannot cleanly close down the app in 3 seconds, 
     // channel is locked due to channel heavily in use 
     // through callbacks or the like. 
     // Throw TimeoutException 
     channel.Close(new TimeSpan(0, 0, 0, 3)); 
    } 
} 

public void Dispose() 
{ 
    // Dispose object 
    if (channel != null) 
    { 
     try 
     { 
      // Close existing connections 
      // ***************************** 
      // This is the close operation where we perform 
      //the channel close and timeout check and catch the exception. 
      Close(); 

      // Attempt dispose object 
      ((IDisposable)channel).Dispose(); 
     } 
     catch (CommunicationException) 
     { 
      channel.Abort(); 
     } 
     catch (TimeoutException) 
     { 
      channel.Abort(); 
     } 
     catch (Exception) 
     { 
      channel.Abort(); 
      throw; 
     } 
    } 
} 

Jestem bardzo szczęśliwa, że ​​to błąd, w końcu już za sobą! Moja aplikacja jest teraz wyłączana po 3-sekundowym limicie czasu, niezależnie od bieżącego stanu usługi WCF, mam nadzieję, że mogłem pomóc komuś, kto kiedykolwiek cierpi na podobny problem.

Graham

+0

_Taki wyjątek nie miał nic wspólnego z zawieszeniem aplikacji, co było tylko wyjątkowym można bezpiecznie złapać. Czy posiadasz łącze referencyjne, które mówi o bezpieczeństwie połknięcia takich wyjątków w protokole? Mam dokładnie ten sam problem. – lesscode

Powiązane problemy