2012-12-04 11 views
7

Ho eseguito una discreta quantità di ricerche e non sono stato in grado di trovare una soluzione o una risposta a questo. Ho una app di prova che ho scritto per mostrare "il problema". (Sono aperto all'idea che questo sia solo il modo in cui funziona WCF e non c'è nulla che possa fare al riguardo.)Come interrompere un proxy WCF bloccato in Stato connessione perché il servizio remoto non è in esecuzione

La mia app di test è un semplice servizio WCF e client con un proxy generato da Visual Studio. Indico il proxy a una macchina remota che è attiva, ma non esegue il servizio. Questo punto è importante. Se la macchina remota non è in esecuzione, la chiamata WCF non riesce immediatamente.

L'app client crea un'istanza della classe proxy client, avvia una nuova attività e quindi tenta di effettuare una chiamata sull'interfaccia del servizio, che verrà bloccata perché il servizio all'altra estremità non è presente. L'attività in background si attiva per 5 secondi e quindi chiude il proxy. Mi aspettavo che l'altro thread venisse immediatamente a mancare quando si chiudeva il proxy, ma non è così. Dopo circa 20 secondi dall'effettuare la chiamata iniziale, si verifica quindi un errore.

Ecco il mio codice cliente:

using System; 
using System.ServiceModel; 
using System.Threading; 
using System.Threading.Tasks; 
using TestConsoleClient.MyTestServiceReference; 

namespace TestConsoleClient 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     MyTestServiceClient client = new MyTestServiceClient(); 

     // Start new thread and wait a little bit before trying to close connection. 
     Task.Factory.StartNew(() => 
     { 
      LogMessage("Sleeping for 5 seconds ..."); 
      Thread.Sleep(5000); 
      if (client == null) 
      { 
       LogMessage("Woke up! Proxy was closed before waking up."); 
       return; 
      } 

      LogMessage("Woke up! Attempting to abort connection."); 
      LogMessage(string.Format("Channel state before Close: {0}", 
       ((ICommunicationObject)client).State)); 

      client.Close(); 
      ((IDisposable)client).Dispose(); 
      client.Abort(); 

      // Channel state is Closed, as expected, but blocked thread does not wake up? 
      LogMessage(string.Format("Channel state after Abort: {0}", 
       ((ICommunicationObject)client).State)); 

      client = null; 
     }); 

     // Start connecting. 
     LogMessage("Starting connection ..."); 

     try 
     { 
      LogMessage("Calling MyTestService.DoWork()"); 
      client.DoWork(); // Timeout is 60 seconds. State is "Connecting" 
     } 
     catch (Exception ex) 
     { 
      LogMessage(string.Format("Exception caught: {0}", ex.Message)); 
      if (client != null) 
      { 
       client.Abort(); 
       ((IDisposable) client).Dispose(); 
       client = null; 
      } 
     } 
     finally 
     { 
      if (client != null) 
      { 
       client.Close(); 
       client = null; 
      } 
     } 

     LogMessage("Press Enter to exit ..."); 

     Console.ReadLine(); 
    } 

    private static void LogMessage(string msg) 
    { 
     Console.WriteLine("{0}: [{1}] {2}", 
          DateTime.Now.ToString("hh:mm:ss tt"), Thread.CurrentThread.ManagedThreadId, msg); 
    } 
} 
} 

Ed ecco l'output del mio programma:

01:48:31 PM: [9] Starting connection ... 
01:48:31 PM: [9] Calling MyTestService.DoWork() 
01:48:31 PM: [10] Sleeping for 5 seconds ... 
01:48:36 PM: [10] Woke up! Attempting to abort connection. 
01:48:36 PM: [10] Channel state before Close: Opening 
01:48:36 PM: [10] Channel state after Abort: Closed 
01:48:54 PM: [9] Exception caught: Could not connect to net.tcp://th-niconevm:80 
80/MyTestService. The connection attempt lasted for a time span of 00:00:22.0573 
009. TCP error code 10060: A connection attempt failed because the connected par 
ty did not properly respond after a period of time, or established connection fa 
iled because connected host has failed to respond 10.10.10.10:8080. 
01:48:54 PM: [9] Press Enter to exit ... 

Quello che sto cercando di realizzare è lasciare che l'utente interrompere la connessione in modo che possano apportare le modifiche se necessario, quindi riprovare. Come puoi vedere nell'output, il canale passa dallo stato "Apertura" allo stato "Chiuso" in seguito, ma la chiamata di servizio rimane bloccata finché non scade. Posso certamente aggirarlo, ma sembra che ci debba essere un modo per interromperlo.

Ecco l'app.config client:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
<system.serviceModel> 
    <bindings> 
     <netTcpBinding> 
      <binding name="NetTcpBinding_IMyTestService" closeTimeout="00:01:00" 
       openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" 
       transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" 
       hostNameComparisonMode="StrongWildcard" listenBacklog="10" 
       maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10" 
       maxReceivedMessageSize="65536"> 
       <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" 
        maxBytesPerRead="4096" maxNameTableCharCount="16384" /> 
       <reliableSession ordered="true" inactivityTimeout="00:10:00" 
        enabled="false" /> 
       <security mode="Transport"> 
        <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" /> 
        <message clientCredentialType="Windows" /> 
       </security> 
      </binding> 
     </netTcpBinding> 
    </bindings> 
    <client> 
     <endpoint address="net.tcp://remotehost:8080/MyTestService" binding="netTcpBinding" 
      bindingConfiguration="NetTcpBinding_IMyTestService" contract="MyTestServiceReference.IMyTestService" 
      name="NetTcpBinding_IMyTestService"> 
      <identity> 
       <userPrincipalName value="remotehost\ClientUser" /> 
      </identity> 
     </endpoint> 
    </client> 
</system.serviceModel> 
</configuration> 
+0

E la configurazione vincolante? Ricevere in esclusiva Timeout? – sll

+0

Aggiunta del client app.config alla domanda originale ... –

+0

In un'attività Azione provare a passare in timeout esplicito per una chiamata 'Close()': 'client.Close (TimeSpan.FromSeconds (2)) ;, così teoricamente dovrebbe chiudere/sollevare un'eccezione dopo max 2 secondi – sll

risposta

0

Si potrebbe ottenere più distanza in miglia se si genera la classe proxy con operazioni asincrone, quindi il codice diventa molto più semplice.

// Asynchronously call DoWork 
MyTestServiceClient client = new MyTestServiceClient(); 
IAsyncResult iar = client.BeginDoWork(null, null); 

// Sleep, dance, do the shopping, whatever 
Thread.Sleep(5000); 

bool requestCompletedInFiveSeconds = iar.IsCompleted; 

// When you're ready, remember to call EndDoWork to tidy up 
client.EndDoWork(iar); 
client.Close(); 

Se ciò può sembrare utile, leggere il modello asincrono. This MSKB è sorprendentemente utile e mostra diversi modi per scrivere codice asincrono.

Il problema potrebbe effettivamente essere che non è possibile annullare un'operazione WCF in corso; dovresti scrivere il tuo meccanismo di cancellazione per questo. Ad esempio, non sarai in grado di stabilire se una richiesta è effettivamente in corso (vale a dire che l'esecuzione di DoWork deve essere interrotta) o se la latenza di una rete o di un'altra macchina è l'interruzione.

+1

mi sono reso conto abbastanza rapidamente che se l'app era stata originariamente scritta con async in mente, questo non sarebbe stato un problema. Sfortunatamente, sto cercando una base di codice significativa basata su chiamate WCF sincrone (TCP bidirezionale) e sarebbe una notevole quantità di lavoro per modificare il framework e il livello di presentazione che lo utilizza. Mi rende triste, ma è lì che sono. Per far apparire la mia domanda originale, non c'è davvero alcun modo di interrompere la connessione prima del timeout? A livello socket nativo, la chiusura di un handle di socket interromperà tutte le chiamate bloccate. Quindi, so che è possibile. –

+0

Sfortunatamente per te, io non la penso così. È necessario richiamare 'DoWork' in modo asincrono in modo da avere il controllo. Puoi capovolgere il tuo codice in modo che invece di spawnare un'attività per monitorare il client, si genera un'attività per chiamare 'DoWork'? È quindi possibile monitorare tale attività e, se impiega troppo tempo, eliminarla. – batwad

Problemi correlati