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>
E la configurazione vincolante? Ricevere in esclusiva Timeout? – sll
Aggiunta del client app.config alla domanda originale ... –
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