Condividi tramite


Sink e catene dei sink

Ogni messaggio viene inviato dai canali lungo una catena di oggetti sink di canale prima di inviare un messaggio o dopo averlo ricevuto. Questa catena di sink contiene i sink necessari per le funzionalità di base del canale, come i sink del formattatore, di trasporto o del generatore di stack, ma è possibile personalizzare la catena dei sink di canale per eseguire attività speciali con un messaggio o un flusso.

Ogni sink di canale implementa IClientChannelSink o IServerChannelSink. È anche necessario che il primo sink di canale sul lato client implementi IMessageSink. In genere viene implementato IClientFormatterSink, che eredita da ImessageSink, IChannelSinkBase e IClientChannelSink ed è denominato sink del formattatore in quanto consente di trasformare il messaggio in arrivo in un flusso, ossia in un oggetto IMessage.

Qualsiasi messaggio inviato a o da un dominio di applicazione viene elaborato dalla catena di sink di canale. Con il messaggio ottenuto è possibile eseguire varie operazioni e nelle successive elaborazioni verrà utilizzato il messaggio restituito al sistema dopo l'elaborazione. È possibile implementare in questa posizione un servizio di registrazione, qualsiasi tipo di filtro, sistemi di crittografia o altre misure di protezione su client o server. Nell'illustrazione riportata di seguito viene mostrata la struttura di una catena dei sink di canale di base.

Catena dei sink di canale di base

Catena dei sink di canale di base

Ciascun sink di canale consente di elaborare il flusso e di passarlo al sink di canale successivo, in modo da indicare agli oggetti precedenti o successivi al sink quali operazioni eseguire con il flusso passato.

Nota

I sink dei messaggi non devono generare eccezioni. A tal fine, inserire il codice di metodo in blocchi try-catch.

I provider di sink di canale, vale a dire gli oggetti che implementano l'interfaccia IClientChannelSinkProvider, IClientFormatterSinkProvider o IServerChannelSinkProvider, sono responsabili della creazione dei sink di canale attraverso i quali scorrono i messaggi di .NET Remoting. Quando viene attivato un tipo remoto, il provider di sink di canale viene recuperato dal canale e il metodo CreateSink viene chiamato su tale provider di sink per recuperare il primo sink di canale dalla catena.

I sink di canale sono responsabili del trasporto di messaggi tra client e server e sono collegati in una catena. Quando il metodo CreateSink viene chiamato su un provider di sink, deve eseguire le operazioni riportate di seguito.

  • Creare il proprio sink di canale.

  • Chiamare CreateSink sul provider di sink successivo nella catena.

  • Assicurarsi che il sink successivo e quello corrente siano collegati.

  • Restituire il sink al chiamante.

I sink di canale hanno la funzione di inoltrare al sink successivo nella catena tutte le chiamate effettuate su di essi, oltre a offrire un meccanismo per la memorizzazione di un riferimento al sink successivo.

I sink di canale sono caratterizzati da una grande flessibilità relativamente agli oggetti inviati sulla catena di sink. I sink di protezione che negoziano l'autenticazione prima di inviare l'effettivo messaggio originale serializzato, ad esempio, possono mantenere il messaggio di canale completo, sostituire il flusso di contenuto con il proprio contenuto e inviarlo sulla catena di sink e verso il dominio di applicazione remoto. Nel viaggio di ritorno il messaggio di risposta può essere intercettato dal sink di protezione, dando origine a una conversazione con i sink di protezione corrispondenti nel dominio di applicazione remoto. Una volta raggiunto un accordo, il sink di protezione di origine può inviare il flusso di contenuto originale al dominio di applicazione remoto.

Elaborazione di messaggi nella catena di sink di canale

Dopo l'individuazione da parte del sistema .NET Remoting di un canale in grado di elaborare l'implementazione di IMethodCallMessage, il messaggio viene passato dal canale al sink di canale del formattatore mediante la chiamata a IMessageSink.SyncProcessMessage o a IMessageSink.AsyncProcessMessage. Il sink del formattatore crea la matrice di intestazione del trasporto e chiama IClientChannelSink.GetRequestStream sul sink successivo. La chiamata viene inoltrata lungo la catena del sink e qualunque sink può creare un flusso di richiesta che verrà passato al sink del formattatore. Se GetRequestStream restituisce un riferimento null (Nothing in Visual Basic), il sink del formattatore crea il proprio sink da utilizzare per la serializzazione. Una volta restituita la chiamata, il messaggio viene serializzato e il metodo appropriato per l'elaborazione del messaggio è chiamato sul primo sink di canale nella catena.

I sink non possono scrivere dati nel flusso, ma sono in grado di leggere dal flusso o passarne uno nuovo se richiesto. Consentono anche di aggiungere intestazioni alle matrici di intestazione, se in precedenza non è stata effettuata una chiamata a GetRequestStream sul sink successivo, e possono essere aggiunti allo stack di sink prima di inoltrare la chiamata al sink successivo. Quando la chiamata raggiunge il sink di trasporto alla fine della catena, questo invia le intestazioni e il messaggio serializzato sul canale al server dove viene invertito l'intero processo. Il sink di trasporto sul lato server recupera le intestazioni e il messaggio serializzato dal lato server del flusso e li inoltra sulla catena di sink fino a raggiungere il sink del formattatore. Il messaggio viene deserializzato dal sink del formattatore e inoltrato al sistema .NET Remoting dove diventa una chiamata di metodo e viene richiamato sull'oggetto server.

Creazione di catene dei sink di canale

Per creare un nuovo sink di canale, è necessario implementare e configurare il sistema .NET Remoting in modo che riconosca un'implementazione di IServerChannelSinkProvider o IClientChannelSinkProvider, con i quali è possibile creare l'implementazione personalizzata IClientChannelSink o IServerChannelSink oppure recuperare il sink successivo nella catena. È possibile utilizzare la classe abstract BaseChannelSinkWithProperties per facilitare l'implementazione dei sink di canale personalizzati.

Generazione di un provider di sink di canale

Le applicazioni possono fornire i provider di sink di canale client o server come parametri durante la costruzione di un canale. È necessario memorizzare i provider di sink di canale in una catena e l'utente dovrà concatenare tutti i provider prima di passare quello esterno al costruttore di canale. A questo scopo, il provider di sink di canale implementa una proprietà Next. Nell'esempio di codice riportato di seguito viene illustrato come generare un provider di sink di canale sul lato client. Un esempio completo è disponibile nella sezione Esempio di codice di .NET Remoting: provider di sink di canale.

private Function CreateDefaultClientProviderChain() As IClientChannelSinkProvider
   Dim chain As New FirstClientFormatterSinkProvider            
   Dim sink As IClientChannelSinkProvider
   sink = chain
   sink.Next = New SecondClientFormatterSinkProvider
   sink = sink.Next
   return chain
End Function 
private IClientChannelSinkProvider CreateDefaultClientProviderChain(){
   IClientChannelSinkProvider chain = new FirstClientFormatterSinkProvider();            
   IClientChannelSinkProvider sink = chain;
   sink.Next = new SecondClientFormatterSinkProvider();
   sink = sink.Next;
   return chain;
} 

Nota

Quando in un file di configurazione sono presenti più provider di sink di canale, il sistema .NET Remoting li concatena nell'ordine in cui si presentano in tale file. I provider di sink di canale vengono creati durante la creazione del canale nel corso della chiamata a RemotingConfiguration.Configure.

Sink del formattatore

Il messaggio del canale viene serializzato nel flusso del messaggio dai sink del formattatore come oggetto che implementa IMessage. In alcune implementazioni di sink del formattatore vengono utilizzati i tipi di formattatori forniti dal sistema, BinaryFormatter e SoapFormatter. Altre implementazioni utilizzano i propri mezzi per trasformare il messaggio del canale nel flusso.

La funzione del sink del formattatore consiste nel generare le intestazioni necessarie e nel serializzare il messaggio nel flusso. Dopo il sink del formattatore, il messaggio viene inoltrato a tutti i sink della catena mediante le chiamate a IMessageSink.ProcessMessage oppure a Imessagesink.AsyncProcessMessage. In questa fase, il messaggio è già stato serializzato e viene fornito solo come informazione.

Nota

I sink mediante i quali è necessario creare o modificare il messaggio devono essere collocati nella catena di sink prima del formattatore. Questo risultato si ottiene facilmente con l'implementazione di IClientFormatterSink, mediante cui il sistema ritiene di avere un riferimento al sink del formattatore. Il vero sink del formattatore può essere inserito più avanti nella catena di sink.

Nel viaggio di ritorno, il flusso del messaggio viene trasformato dal sink del formattatore negli elementi del messaggio del canale (messaggio restituito). Il primo sink sul lato client deve implementare l'interfaccia IClientFormatterSink. Quando CreateSink viene restituito al canale, si esegue il cast del riferimento restituito in un tipo IClientFormatterSink in modo da poter chiamare SyncProcessMessage dell'interfaccia IMessage. Se il cast non ha esito positivo, viene generata un'eccezione.

Sink di canale personalizzati

Sul lato client i sink di canale personalizzati vengono inseriti nella catena di oggetti tra il sink del formattatore e l'ultimo sink di trasporto. Con l'inserimento di un sink di canale personalizzato nel canale client o server è possibile elaborare IMessage in una delle due situazioni elencate di seguito.

  • Durante il processo che converte in un flusso e invia sulla connessione una chiamata rappresentata come messaggio.

  • Durante il processo mediante il quale un flusso viene eliminato dalla connessione per essere inviato all'oggetto StackBuilderSink, ovvero all'ultimo sink di messaggi prima dell'oggetto remoto sul server, o all'oggetto proxy sul client.

I sink personalizzati possono leggere o scrivere dati nel flusso, a seconda che la chiamata sia in uscita o in ingresso, e aggiungere eventuali altre informazioni alle intestazioni. In questa fase, il messaggio è già stato serializzato dal formattatore e non può essere modificato. Quando la chiamata del messaggio è inoltrata al sink di trasporto alla fine della catena, le intestazioni vengono scritte dal sink di trasporto nel flusso, il quale viene inoltrato al sink di trasporto sul server mediante il protocollo di trasporto imposto dal canale.

Sink di trasporto

Il sink di trasporto è l'ultimo sink della catena sul lato client e il primo della catena sul lato server. Oltre a trasportare il messaggio serializzato, il sink di trasporto invia anche le intestazioni al server e recupera il flusso e le intestazioni quando la chiamata viene restituita dal server. Questi sink vengono generati nel canale e non possono essere estesi.

Sostituzione del formattatore predefinito

Poiché un canale è un meccanismo di rete astratto, è possibile configurare il sistema .NET Remoting per combinare un canale implementato dal sistema con qualsiasi formattatore scelto. A tal fine si può utilizzare il costruttore di canale che accetta un'implementazione di IDictionary di proprietà del canale, un formattatore sul lato server e uno sul lato client. È anche possibile specificare il formattatore in un file di configurazione. Nell'esempio seguente viene illustrato come creare una classe HttpChannel mediante il sistema di configurazione di .NET Remoting utilizzando però BinaryClientFormatterSink sul lato client.

<configuration>
   <system.runtime.remoting>
      <application>
         <channels>
            <channel ref="http">
               <clientProviders>
                  <formatter ref="binary"/>
               </clientProviders>
         <channels>
      </application>
   </system.runtime.remoting>
</configuration> 

Nell'esempio di codice che segue le stesse operazioni vengono eseguite a livello di programmazione, presupponendo un tipo di interfaccia remota IService che consente di implementare GetServerString e GetServerTime.

Imports System
Imports System.Collections
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http

Public Class ClientProcess
  <MTAThread()> _
  Public Shared Sub Main()
      
    ' Note that any name/value pairs of configuration attributes can be 
    ' placed in this dictionary (the configuration system calls this same 
    ' constructor).
    Dim properties As New Hashtable()
    properties("name") = "HttpBinary"
     
    ChannelServices.RegisterChannel(New HttpChannel(properties, New BinaryClientFormatterSinkProvider(), Nothing))
    ' The last parameter above (Nothing) is the server sink provider chain 
    ' to obtain the default behavior (which includes SOAP and 
    ' binary formatters on the server side).
    Dim service As IService = CType(Activator.GetObject(GetType(IService), "http://computer:8080/SAService"), IService)
      
    Console.WriteLine("Server string is: " + service.GetServerString())
    Console.WriteLine("Server time is: " + service.GetServerTime())
  End Sub
   
End Class 
using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

public class ClientProcess{

  public static void Main(string[] Args){
        
    // Note that any name/value pairs of configuration attributes can be 
    // placed in this dictionary (the configuration system calls this 
    // same HttpChannel constructor).
    IDictionary properties = new Hashtable();
    properties["name"] = "HttpBinary";

    // The last parameter below is the server sink provider chain 
    // to obtain the default behavior (which includes SOAP and binary 
    // formatters) on the server side.
    ChannelServices.RegisterChannel(new HttpChannel(null, new BinaryClientFormatterSinkProvider(), null));

    IService service = (IService)Activator.GetObject(typeof(IService),"http://computer:8080/SAService");
        
    Console.WriteLine("Server string is: " + service.GetServerString());
    Console.WriteLine("Server time is: " + service.GetServerTime());      
  }
}

Per un esempio completo della combinazione qui definita di canale e formattatore ospitata in Internet Information Services (IIS), vedere Esempio di codice di .NET Remoting: hosting in Internet Information Services (IIS).

Per modificare il client specificato in modo che utilizzi un oggetto TcpChannel con l'oggetto SoapClientFormatterSink, è sufficiente modificare gli spazi dei nomi e la chiamata a RegisterChannel , come illustrato nell'esempio di codice seguente.

ChannelServices.RegisterChannel(New TcpChannel(properties, New SoapClientFormatterSinkProvider(), Nothing))
ChannelServices.RegisterChannel(new TcpChannel(null, new SoapClientFormatterSinkProvider(), null));

Vedere anche

Concetti

Hosting di oggetti remoti in Internet Information Services (IIS)
Esempio di codice di .NET Remoting: hosting in Internet Information Services (IIS)

Altre risorse

Opzioni avanzate di .NET Remoting