Trasporto: Interoperabilità WSE 3.0 TCP

L'esempio Trasporto: Interoperabilità WSE 3.0 TCP illustra come implementare una sessione duplex TCP come trasporto WCF (Windows Communication Foundation) personalizzato. Illustra anche come utilizzare l'estendibilità del livello del canale per connettersi via cavo con sistemi distribuiti esistenti. I passaggi seguenti illustrano come compilare questo trasporto WCF personalizzato:

  1. A partire da un socket TCP, creare client e implementazioni server di IDuplexSessionChannel che utilizzano framing DIME per delineare i limiti del messaggio.

  2. Creare una channel factory che si connette a un servizio TCP WSE e invia messaggi con limiti nelle classi IDuplexSessionChannel del client.

  3. Creare un listener del canale per accettare connessioni TCP in ingresso e produrre canali corrispondenti.

  4. Assicurarsi che eventuali eccezioni specifiche della rete vengano normalizzate nella classe derivata appropriata di CommunicationException.

  5. Inserire un elemento di associazione che aggiunge il trasporto personalizzato a uno stack di canali. Per altre informazioni, vedere [Aggiunta di un elemento di binding].

Creazione di IDuplexSessionChannel

Il primo passaggio per scrivere il trasporto interoperabilità WSE 3.0 TCP consiste nel creare un'implementazione di IDuplexSessionChannel su Socket. WseTcpDuplexSessionChannel deriva da ChannelBase. La logica dell'invio di un messaggio è costituita da due parti principali: (1) codifica del messaggio in byte e (2) framing dei byte e invio via cavo.

ArraySegment<byte> encodedBytes = EncodeMessage(message);

WriteData(encodedBytes);

Inoltre, viene utilizzato un blocco in modo che le chiamate Send() mantengano la garanzia di ordine di IDuplexSessionChannel e per fare sì che le chiamate al socket sottostante siano sincronizzate correttamente.

WseTcpDuplexSessionChannel utilizza una classe MessageEncoder per tradurre classi Message da e verso byte[]. Poiché si tratta di un trasporto, WseTcpDuplexSessionChannel è inoltre responsabile dell'applicazione dell'indirizzo remoto con cui il canale è stato configurato. EncodeMessage contiene la logica per la conversione.

this.RemoteAddress.ApplyTo(message);

return encoder.WriteMessage(message, maxBufferSize, bufferManager);

Dopo essere stata codificata in byte, la classe Message deve essere trasmessa via cavo. Ciò richiede un sistema per la definizione dei limiti del messaggio. WSE 3.0 usa una versione di DIME come protocollo di framing. WriteData contiene la logica di framing per eseguire il wrapping di un byte[] in un set di record DIME.

La logica alla base della ricezione dei messaggi è simile. Il problema più complesso sta nel gestite il fatto che la lettura di un socket può restituire meno byte di quanto richiesto. Per ricevere un messaggio, WseTcpDuplexSessionChannel legge i byte via cavo, decodifica il framing DIME e utilizza MessageEncoder per tradurre i byte [] in una classe Message.

WseTcpDuplexSessionChannel di base presuppone di ricevere un socket collegato. La classe di base gestisce l'arresto del socket. Ci sono tre posizioni che interfacciano con la chiusura del socket:

  • OnAbort -- chiude il socket in modo forzato (chiusura forzata).

  • On[Begin]Close -- chiude il socket normalmente (chiusura normale).

  • session.CloseOutputSession: arrestare il flusso di dati in uscita (metà chiusura).

Channel Factory

Il passaggio successivo per scrivere un trasporto TCP consiste nel creare un'implementazione di IChannelFactory per i canali client.

  • WseTcpChannelFactory deriva da ChannelFactoryBase<IDuplexSessionChannel>. È una factory che esegue l'override di OnCreateChannel per produrre canali client.

protected override IDuplexSessionChannel OnCreateChannel(EndpointAddress remoteAddress, Uri via)

{

return new ClientWseTcpDuplexSessionChannel(encoderFactory, bufferManager, remoteAddress, via, this);

}

  • ClientWseTcpDuplexSessionChannel aggiunge logica al WseTcpDuplexSessionChannel di base per connettersi a un server TCP in fase di channel.Open. Il nome host viene innanzitutto risolto in un indirizzo IP, come illustrato nel codice seguente.

hostEntry = Dns.GetHostEntry(Via.Host);

  • Il nome host viene quindi connesso al primo indirizzo IP disponibile in un ciclo, come illustrato nel codice seguente.

IPAddress address = hostEntry.AddressList[i];

socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

socket.Connect(new IPEndPoint(address, port));

  • Come parte del contratto del canale, viene eseguito l'incapsulamento delle eccezioni specifiche del dominio, ad esempio SocketException in CommunicationException.

Channel Listener

Il passaggio successivo per scrivere un trasporto TCP consiste nel creare un'implementazione di IChannelListener per accettare canali server.

  • WseTcpChannelListener deriva da ChannelListenerBase<IDuplexSessionChannel> ed esegue l'override di On[Begin]Open e On[Begin]Close per controllare la durata del socket di ascolto. In OnOpen, un socket viene creato per ascoltare su IP_ANY. Implementazioni più avanzate possono creare un secondo socket per ascoltare anche su IPv6. Possono consentire anche che l'indirizzo IP sia specificato nel nome host.

IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Any, uri.Port);

this.listenSocket = new Socket(localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

this.listenSocket.Bind(localEndpoint);

this.listenSocket.Listen(10);

Quando un nuovo socket viene accettato, viene inizializzato un canale server con esso. Tutti gli input e output sono già implementati nella classe di base, pertanto questo canale è responsabile dell'inizializzazione del socket.

Aggiunta di un elemento di associazione.

Ora che le factory e i canali sono compilati, devono essere esposti al runtime di ServiceModel tramite un'associazione. Un'associazione è una raccolta di elementi di associazione che rappresentano lo stack di comunicazione associato a un indirizzo del servizio. Ogni elemento dello stack è rappresentato da un elemento di associazione.

Nell'esempio, l'elemento di associazione è WseTcpTransportBindingElement, che deriva dalla classe TransportBindingElement. Esso supporta la classe IDuplexSessionChannel ed esegue l'override dei metodi seguenti per compilare le factory associate all'associazione.

public IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)

{

return (IChannelFactory<TChannel>)(object)new WseTcpChannelFactory(this, context);

}

public IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)

{

return (IChannelListener<TChannel>)(object)new WseTcpChannelListener(this, context);

}

Contiene inoltre membri per duplicare BindingElement e restituire lo schema (wse.tcp).

Console per eseguire il test di TCP WSE

Codice per testare l'utilizzo di questo esempio di trasporto è disponibile in TestCode.cs. Nelle istruzioni seguenti viene illustrato come configurare l'esempio TcpSyncStockService WSE.

Il codice di prova crea un'associazione personalizzata che utilizza MTOM come codifica e WseTcpTransport come trasporto. Configura inoltre la versione di indirizzamento in modo che sia conforme a WSE 3.0, come illustrato nel codice seguente.

CustomBinding binding = new CustomBinding();

MtomMessageEncodingBindingElement mtomBindingElement = new MtomMessageEncodingBindingElement();

mtomBindingElement.MessageVersion = MessageVersion.Soap11WSAddressingAugust2004;

binding.Elements.Add(mtomBindingElement);

binding.Elements.Add(new WseTcpTransportBindingElement());

È costituito da due test. Il primo imposta un client tipizzato utilizzando codice generato da WSE 3.0 WSDL. Il secondo usa WCF come client e server, inviando direttamente messaggi sul canale API.

Quando si esegue l'esempio, viene visualizzato l'output seguente:

Client:

Calling soap://stockservice.contoso.com/wse/samples/2003/06/TcpSyncStockService

Symbol: FABRIKAM
        Name: Fabrikam, Inc.
        Last Price: 120

Symbol: CONTOSO
        Name: Contoso Corp.
        Last Price: 50.07
Press enter.

Received Action: http://SayHello
Received Body: to you.
Hello to you.
Press enter.

Received Action: http://NotHello
Received Body: to me.
Press enter.

Server:

Listening for messages at soap://stockservice.contoso.com/wse/samples/2003/06/TcpSyncStockService

Press any key to exit when done...

Request received.
Symbols:
        FABRIKAM
        CONTOSO

Impostare, compilare ed eseguire l'esempio

  1. Per eseguire questo esempio, è necessario disporre di Web Services Enhancements (WSE) 3.0 per Microsoft .NET e l'esempio di WSE TcpSyncStockService installato.

Nota

Poiché WSE 3.0 non è supportato in Windows Server 2008, non è possibile installare o eseguire l'esempio di TcpSyncStockService in tale sistema operativo.

  1. Dopo aver installato l'esempio TcpSyncStockService, eseguire le operazioni seguenti:

    1. Aprire TcpSyncStockService in Visual Studio. L'esempio TcpSyncStockService è installato con WSE 3.0. Non fa parte del codice di questo esempio.

    2. Impostare il progetto StockService come progetto di avvio.

    3. Aprire StockService.cs nel progetto StockService e impostare come commento l'attributo [Policy] sulla classe StockService. Ciò disabilita la sicurezza dell'esempio. Mentre WCF può interoperare con gli endpoint protetti WSE 3.0, la sicurezza è stata disabilitata in modo che l'esempio sia focalizzato sul trasporto TCP personalizzato.

    4. premere F5 per avviare TcpSyncStockService. Il servizio si avvia in una nuova finestra della console.

    5. Aprire questo esempio di trasporto TCP in Visual Studio.

    6. Aggiornare la variabile "nome host" in TestCode.cs e farla corrispondere al nome del computer che esegue TcpSyncStockService.

    7. premere F5 per avviare l'esempio di trasporto TCP.

    8. Il client di prova del trasporto TCP viene inizializzato in una nuova console. Il client richiede quotazioni al servizio e quindi visualizza i risultati nella finestra della console.