Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
In questo argomento vengono illustrati gli stati e le transizioni di cui dispongono i canali, i tipi usati per strutturare gli stati del canale e come implementarli.
Macchine a stati e canali
Gli oggetti che gestiscono la comunicazione, ad esempio i socket, in genere presentano una macchina a stati le cui transizioni di stato sono correlate all'allocazione delle risorse di rete, alla creazione o all'accettazione di connessioni, alla chiusura delle connessioni e alla terminazione della comunicazione. La macchina a stati del canale fornisce un modello uniforme degli stati di un oggetto di comunicazione che astrae l'implementazione sottostante di tale oggetto. L'interfaccia ICommunicationObject fornisce un set di stati, metodi di transizione dello stato ed eventi di transizione dello stato. Tutti i canali, le fabbriche di canali e gli ascoltatori di canali implementano la macchina a stati del canale.
Gli eventi Closed, Closing, Faulted, Opened e Opening segnalano un osservatore esterno dopo che si verifica una transizione di stato.
I metodi Abortire, Chiudi e Aprire (e i loro equivalenti asincroni corrispondenti) causano transizioni di stato.
La proprietà state restituisce lo stato corrente come definito da CommunicationState:
ICommunicationObject, CommunicationObject e Stati e transizione degli stati
Un ICommunicationObject oggetto inizia nello stato Created in cui è possibile configurare le varie proprietà. Una volta nello stato Opened, l'oggetto è utilizzabile per l'invio e la ricezione di messaggi, ma le relative proprietà sono considerate non modificabili. Una volta nello stato Closing, l'oggetto non può più elaborare nuove richieste di invio o ricezione, ma le richieste esistenti hanno la possibilità di completare fino a quando non viene raggiunto il timeout di chiusura. Se si verifica un errore irreversibile, l'oggetto passa allo stato Faulted in cui può essere controllato per ottenere informazioni sull'errore e infine chiuso. Quando l'oggetto è nello stato di Chiusura ha essenzialmente raggiunto la fine del ciclo della macchina a stati. Una volta che un oggetto passa da uno stato a quello successivo, non torna a uno stato precedente.
Il diagramma seguente mostra gli stati e le ICommunicationObject transizioni di stato. Le transizioni di stato possono essere causate chiamando uno dei tre metodi: Interruzione, Apertura o Chiusura. Potrebbero anche essere causati dalla chiamata di altri metodi specifici dell'implementazione. La transizione allo stato Faulted può verificarsi a causa di errori durante l'apertura o dopo l'apertura dell'oggetto di comunicazione.
Ogni ICommunicationObject inizia nello stato Creato. In questo stato, un'applicazione può configurare l'oggetto impostandone le proprietà. Una volta che un oggetto è in uno stato diverso da Created, viene considerato non modificabile.
Figura 1. La macchina a stati di ICommunicationObject.
Windows Communication Foundation (WCF) fornisce una classe base astratta denominata CommunicationObject che implementa ICommunicationObject e la macchina a stati del canale. L'immagine seguente è un diagramma di stato modificato specifico di CommunicationObject. Oltre alla macchina a stati ICommunicationObject, mostra la tempistica in cui vengono invocati i metodi aggiuntivi CommunicationObject.
Figura 2. Implementazione di CommunicationObject della macchina a stati ICommunicationObject, comprensiva delle chiamate agli eventi e ai metodi protetti.
Eventi di ICommunicationObject
CommunicationObject espone i cinque eventi definiti da ICommunicationObject. Questi eventi sono progettati per il codice che usa l'oggetto di comunicazione per ricevere una notifica delle transizioni di stato. Come illustrato nella Figura 2 sopra menzionata, ogni evento viene generato una singola volta dopo la transizione dello stato dell'oggetto nello stato indicato dall'evento. Tutti e cinque gli eventi sono del EventHandler tipo definito come:
public delegate void EventHandler(object sender, EventArgs e);
Nell'implementazione CommunicationObject , il mittente è l'oggetto CommunicationObject stesso o qualsiasi elemento passato come mittente al costruttore (se è stato usato l'overload del CommunicationObject costruttore). Il parametro EventArgs, e, è sempre EventArgs.Empty.
Callback di oggetti derivati
Oltre ai cinque eventi, CommunicationObject dichiara otto metodi virtuali protetti progettati per consentire il rientro di un oggetto derivato prima e dopo la transizione dello stato.
I CommunicationObject.Open metodi e CommunicationObject.Close dispongono di tre callback associati a ognuno di essi. Ad esempio, corrispondente a CommunicationObject.Open ci sono CommunicationObject.OnOpening, CommunicationObject.OnOpen e CommunicationObject.OnOpened. Sono associati a CommunicationObject.Close i metodi CommunicationObject.OnClose, CommunicationObject.OnClosing e CommunicationObject.OnClosed.
Analogamente, il CommunicationObject.Abort metodo ha un oggetto corrispondente CommunicationObject.OnAbort.
Mentre CommunicationObject.OnOpen, CommunicationObject.OnClose e CommunicationObject.OnAbort non hanno implementazione predefinita, gli altri callback hanno un'implementazione predefinita necessaria per la correttezza del funzionamento della macchina a stati. Se si esegue l'override di questi metodi, assicurarsi di chiamare l'implementazione di base o sostituirla correttamente.
CommunicationObject.OnOpening, CommunicationObject.OnClosing e CommunicationObject.OnFaulted generano gli eventi corrispondenti CommunicationObject.Opening, CommunicationObject.Closing e CommunicationObject.Faulted. CommunicationObject.OnOpened e CommunicationObject.OnClosed impostano lo stato dell'oggetto rispettivamente su CommunicationObject.OpenedOpenedCommunicationObject.Opened e Closed, quindi generano gli eventi corrispondenti e .
Metodi di transizione dello stato
CommunicationObject fornisce implementazioni di interruzione, chiusura e apertura. Fornisce inoltre un metodo Fault che causa una transizione di stato allo stato Faulted. La figura 2 mostra la ICommunicationObject macchina a stati con ogni transizione etichettata dal metodo che lo causa (le transizioni senza etichetta vengono eseguite all'interno dell'implementazione del metodo che ha causato l'ultima transizione etichettata).
Annotazioni
Tutte le CommunicationObject implementazioni dell'accesso/impostazione dello stato di comunicazione sono sincronizzate con i thread.
Costruttore
CommunicationObject fornisce tre costruttori, tutti lasciano l'oggetto nello stato Created. I costruttori sono definiti come:
Il primo costruttore è un costruttore senza parametri che delega all'overload del costruttore che accetta un oggetto :
protected CommunicationObject() : this(new object()) { … }
Il costruttore che accetta un oggetto usa tale parametro come oggetto da bloccare durante la sincronizzazione dell'accesso allo stato dell'oggetto di comunicazione:
protected CommunicationObject(object mutex) { … }
Infine, un terzo costruttore accetta un parametro aggiuntivo, utilizzato come argomento del mittente quando vengono generati eventi ICommunicationObject.
protected CommunicationObject(object mutex, object eventSender) { … }
I due costruttori precedenti hanno impostato il mittente a questo.
Metodo di apertura
Precondizione: viene creato lo stato.
Post-condizione: lo stato è Aperto o Con errore. Può generare un'eccezione.
Il metodo Open() tenterà di aprire l'oggetto di comunicazione e di impostare lo stato su Opened. Se si verifica un errore, lo stato verrà impostato su Faulted.
Il metodo verifica innanzitutto che lo stato corrente sia Creato. Se lo stato corrente è Opening o Opened, genera un'eccezione InvalidOperationException. Se lo stato attuale è Closing o Closed, genera un'eccezione CommunicationObjectAbortedException se l'oggetto è stato terminato; altrimenti ObjectDisposedException. Se lo stato corrente è Faulted, genera un'eccezione CommunicationObjectFaultedException.
Imposta quindi lo stato su Opening e chiama OnOpening() (che genera l'evento Opening), OnOpen() e OnOpened() in tale ordine. OnOpened() imposta lo stato su Opened e genera l'evento Opened. Se uno di questi genera un'eccezione, Open() chiama Fault() e consente la propagazione dell'eccezione. Il diagramma seguente mostra il processo Open in modo più dettagliato.
Eseguire l'override del metodo OnOpen per implementare una logica aperta personalizzata, ad esempio l'apertura di un oggetto di comunicazione interno.
Metodo Close
Precondizione: Nessuno.
Post-condizione: Stato è Chiuso. Può generare un'eccezione.
Il metodo Close() può essere chiamato in qualsiasi stato. Tenta di chiudere l'oggetto normalmente. Se viene rilevato un errore, termina l'oggetto . Il metodo non esegue alcuna operazione se lo stato corrente è Closing o Closed. In caso contrario, imposta lo stato su Closing. Se lo stato originale è Creato, In apertura o In errore, invoca Abort() (vedere il diagramma seguente). Se lo stato originale è Stato Aperto, chiama OnClosing() (che genera l'evento Closing), OnClose() e OnClosed() in tale ordine. Se uno di questi genera un'eccezione, Close() chiama Abort() e lascia che l'eccezione si propaghi. OnClosed() imposta lo stato su Closed e genera l'evento Closed. Il diagramma seguente mostra il processo Close in modo più dettagliato.
Eseguire l'override del metodo OnClose per implementare la logica di chiusura personalizzata, ad esempio la chiusura di un oggetto di comunicazione interno. Tutta la logica di chiusura normale che può bloccare per molto tempo (ad esempio, in attesa che l'altro lato risponda) deve essere implementata in OnClose() perché richiede un parametro di timeout e perché non viene chiamato come parte di Abort().
Annulla
Precondizione: Nessuno.
Post-condizione: Stato è Chiuso. Può generare un'eccezione.
Il metodo Abort() non esegue alcuna operazione se lo stato corrente è Closed o se l'oggetto è stato terminato prima( ad esempio, con Abort() in esecuzione su un altro thread. In caso contrario, imposta lo stato su Closing e chiama OnClosing() (che genera l'evento Closing), OnAbort() e OnClosed() in tale ordine (non chiama OnClose perché l'oggetto viene terminato, non chiuso). OnClosed() imposta lo stato su Closed e genera l'evento Closed. Se uno di questi genera un'eccezione, viene rilanciata al chiamante di Abort. Le implementazioni di OnClosing(), OnClosed() e OnAbort() non devono essere bloccanti (ad esempio, su input/output). Il diagramma seguente mostra il processo di interruzione in modo più dettagliato.
Eseguire l'override del metodo OnAbort per implementare la logica di terminazione personalizzata, ad esempio terminando un oggetto di comunicazione interno.
Colpa
Il metodo Fault è specifico di CommunicationObject e non fa parte dell'interfaccia ICommunicationObject . È incluso qui per completezza.
Precondizione: Nessuno.
Post-condizione: lo stato è Difettoso. Può generare un'eccezione.
Il metodo Fault() non esegue alcuna operazione se lo stato corrente è Faulted o Closed. Altrimenti imposta lo stato su Faulted e chiama OnFaulted(), il che genera l'evento Faulted. Se OnFaulted genera un'eccezione, questa viene rilanciata.
Metodi "ThrowIfXxx"
CommunicationObject dispone di tre metodi protetti che possono essere utilizzati per generare eccezioni se l'oggetto si trova in uno stato specifico.
ThrowIfDisposed genera un'eccezione se lo stato è Closing, Closed o Faulted.
ThrowIfDisposedOrImmutable genera un'eccezione se il suo stato non è Creato.
ThrowIfDisposedOrNotOpen genera un'eccezione se lo stato non è Aperto.
Le eccezioni generate dipendono dallo stato. La tabella seguente mostra i diversi stati e il corrispondente tipo di eccezione generato chiamando ThrowIfXxx, quando viene sollevata un'eccezione su quel stato.
| stato | È stato chiamato Abort? | Eccezione |
|---|---|---|
| Creato | Non disponibile | System.InvalidOperationException |
| Apertura | Non disponibile | System.InvalidOperationException |
| Aperto | Non disponibile | System.InvalidOperationException |
| Chiusura | Sì | System.ServiceModel.CommunicationObjectAbortedException |
| Chiusura | NO | System.ObjectDisposedException |
| Chiuso | Sì | System.ServiceModel.CommunicationObjectAbortedException nel caso in cui un oggetto sia stato chiuso da una chiamata precedente ed esplicita di Abort. Se si invoca Close sull'oggetto, viene generata un'eccezione System.ObjectDisposedException. |
| Chiuso | NO | System.ObjectDisposedException |
| In errore | Non disponibile | System.ServiceModel.CommunicationObjectFaultedException |
Timeout
Diversi dei metodi che abbiamo discusso prendono i parametri di timeout. Si tratta di Close, Open (alcuni sovraccarichi e versioni asincrone), OnClose e OnOpen. Questi metodi sono progettati per consentire operazioni lunghe ,ad esempio bloccando l'input/output durante la chiusura normale di una connessione, in modo che il parametro di timeout indichi per quanto tempo tali operazioni possono richiedere prima di essere interrotte. Le implementazioni di uno di questi metodi devono usare il valore di timeout fornito per assicurarsi che restituisca al chiamante entro tale timeout. Le implementazioni di altri metodi che non richiedono un timeout non sono progettate per operazioni lunghe e non devono essere bloccate sull'input/output.
L'eccezione sono gli overload Open() e Close() che non richiedono un timeout. Questi usano un valore di timeout predefinito fornito dalla classe derivata. CommunicationObject espone due proprietà astratte protette denominate DefaultCloseTimeout e DefaultOpenTimeout definite come:
protected abstract TimeSpan DefaultCloseTimeout { get; }
protected abstract TimeSpan DefaultOpenTimeout { get; }
Una classe derivata implementa queste proprietà per fornire il timeout predefinito per gli overload Open() e Close() che non accettano un valore di timeout. Le implementazioni di Open() e Close() delegano quindi all'overload che richiede un timeout passando il valore di timeout predefinito, ad esempio:
public void Open()
{
this.Open(this.DefaultOpenTimeout);
}
IDefaultCommunicationTimeouts
Questa interfaccia dispone di quattro proprietà di sola lettura per fornire valori di timeout predefiniti per aprire, inviare, ricevere e chiudere. Ogni implementazione è responsabile di ottenere i valori predefiniti in qualsiasi modo appropriato. Per praticità, ChannelFactoryBase e ChannelListenerBase impostano questi valori su 1 minuto automaticamente.