Transporte: interoperabilidade de TCP de WSE 3.0
O exemplo de Transporte de interoperabilidade de TCP no WSE 3.0 demonstra como implementar uma sessão duplex de TCP como um transporte personalizado do WCF (Windows Communication Foundation). Também demonstra como você pode usar a extensibilidade da camada de canal para fazer a interface pelo fio com sistemas implantados existentes. As etapas a seguir mostram como criar este transporte personalizado no WCF:
Começando com um soquete de TCP, crie implementações de cliente e servidor de IDuplexSessionChannel que usam o Enquadramento DIME para delinear limites de mensagem.
Crie uma fábrica de canais que se conecte a um serviço TCP do WSE e envie mensagens emolduradas pelos IDuplexSessionChannel do cliente.
Crie um ouvinte de canais para aceitar conexões TCP de entrada e produzir canais correspondentes.
Confirme que quaisquer exceções específicas à rede sejam normalizadas para a classe derivada apropriada de CommunicationException.
Adicione um elemento de associação que acrescenta o transporte personalizado a uma pilha de canais. Para saber mais, confira [Adição de um elemento de associação].
Criação de IDuplexSessionChannel
A primeira etapa na gravação do Transporte de Interoperabilidade TCP no WSE 3.0 é criar uma implementação de IDuplexSessionChannel sobre um Socket. WseTcpDuplexSessionChannel
deriva de ChannelBase. A lógica de enviar uma mensagem consiste em duas partes principais: (1) Codificação da mensagem em bytes e (2) enquadramento desses bytes e enviá-los por transferência.
ArraySegment<byte> encodedBytes = EncodeMessage(message);
WriteData(encodedBytes);
Além disso, um bloqueio é feito para que as chamadas Send() preservem a garantia de ordem de IDuplexSessionChannel, para que as chamadas para o soquete subjacente sejam sincronizadas corretamente.
WseTcpDuplexSessionChannel
usa um MessageEncoder para converter um Message de e para byte[]. Como é um transporte, WseTcpDuplexSessionChannel
também é responsável por aplicar o endereço remoto com o qual o canal foi configurado. EncodeMessage
encapsula a lógica dessa conversão.
this.RemoteAddress.ApplyTo(message);
return encoder.WriteMessage(message, maxBufferSize, bufferManager);
Depois que o Message é codificado em bytes, ele deve ser transmitido. Isso requer um sistema para definição de limites de mensagem. O WSE 3.0 usa uma versão do DIME como seu protocolo de enquadramento. WriteData
encapsula a lógica de enquadramento para encapsular um byte[] em um conjunto de registros DIME.
A lógica para receber mensagens é semelhante. A principal complexidade é lidar com o fato de que uma leitura de soquete pode retornar menos bytes do que o solicitado. Para receber uma mensagem, o WseTcpDuplexSessionChannel
lê bytes na transmissão, decodifica o enquadramento DIME e usa o MessageEncoder para transformar o byte[] em um Message.
A base WseTcpDuplexSessionChannel
pressupõe que ela receba um soquete conectado. A classe base manipula o desligamento do soquete. Há três locais que fazem interface com o fechamento do soquete:
OnAbort -- fecha o soquete de forma desajeitada (fechamento forçado).
On[Begin]Close -- fecha o soquete normalmente (fechamento suave).
session.CloseOutputSession -- desliga o fluxo de dados de saída (meio fechamento).
Fábrica de canais
A próxima etapa na gravação do transporte TCP é criar uma implementação de IChannelFactory para canais do cliente.
WseTcpChannelFactory
deriva de ChannelFactoryBase<IDuplexSessionChannel>. É uma fábrica que substituiOnCreateChannel
para produzir canais de cliente.
protected override IDuplexSessionChannel OnCreateChannel(EndpointAddress remoteAddress, Uri via)
{
return new ClientWseTcpDuplexSessionChannel(encoderFactory, bufferManager, remoteAddress, via, this);
}
ClientWseTcpDuplexSessionChannel
adiciona lógica à baseWseTcpDuplexSessionChannel
para se conectar a um servidor TCP no momento dechannel.Open
. Primeiro, o nome do host é resolvido para um endereço IP, conforme mostrado no código a seguir.
hostEntry = Dns.GetHostEntry(Via.Host);
- Em seguida, o nome do host é conectado ao primeiro endereço IP disponível em um loop, conforme mostrado no código a seguir.
IPAddress address = hostEntry.AddressList[i];
socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(new IPEndPoint(address, port));
- Como parte do contrato de canal, todas as exceções específicas do domínio são encapsuladas, como
SocketException
em CommunicationException.
Ouvinte de canais
A próxima etapa na gravação do transporte TCP é criar uma implementação de IChannelListener para aceitar canais do servidor.
WseTcpChannelListener
deriva de ChannelListenerBase<IDuplexSessionChannel> e substitui On[Begin]Open e On[Begin]Close para controlar o tempo de vida de seu soquete de escuta. No OnOpen, um soquete é criado para escutar em IP_ANY. Implementações mais avançadas também podem criar um segundo soquete para escutar no IPv6. Elas também podem permitir que o endereço IP seja especificado no nome do 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 um novo soquete é aceito, um canal de servidor é inicializado com esse soquete. Entrada e saída já estão implementadas na classe base, portanto, esse canal é responsável por inicializar o soquete.
Adicionar um elemento de associação
Agora que as fábricas e os canais foram criados, eles devem ser expostos ao runtime do ServiceModel por meio de uma associação. Uma associação é uma coleção de elementos de associação que representa a pilha de comunicação associada a um endereço de serviço. Cada elemento na pilha é representado por um elemento de associação.
No exemplo, o elemento de associação é WseTcpTransportBindingElement
, que deriva de TransportBindingElement. Ele dá suporte a IDuplexSessionChannel e substitui os métodos a seguir para criar as fábricas associadas à nossa associação.
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);
}
Também contém membros para clonar o BindingElement
e retornar nosso esquema (wse.tcp).
O console de teste de TCP no WSE
O código de teste para usar esse transporte de exemplo está disponível em TestCode.cs. As instruções a seguir mostram como configurar o exemplo de TcpSyncStockService
do WSE.
O código de teste cria uma associação personalizada que usa MTOM como codificação e WseTcpTransport
como transporte. Também configura o AddressingVersion para estar em conformidade com o WSE 3.0, conforme mostrado no código a seguir.
CustomBinding binding = new CustomBinding();
MtomMessageEncodingBindingElement mtomBindingElement = new MtomMessageEncodingBindingElement();
mtomBindingElement.MessageVersion = MessageVersion.Soap11WSAddressingAugust2004;
binding.Elements.Add(mtomBindingElement);
binding.Elements.Add(new WseTcpTransportBindingElement());
Ele é composto por dois testes – um teste configura um cliente tipado usando o código gerado a partir do WSDL no WSE 3.0. O segundo teste usa o WCF como o cliente e o servidor enviando mensagens diretamente sobre as APIs do canal.
Ao executar o exemplo, a saída a seguir é esperada.
Cliente:
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.
Servidor:
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
Configurar, compilar e executar a amostra
- Para executar este exemplo, você deve ter o WSE (Web Services Enhancements) 3.0 para o Microsoft .NET e o exemplo
TcpSyncStockService
do WSE instalados.
Observação
Como o WSE 3.0 não tem suporte no Windows Server 2008, você não pode instalar ou executar o exemplo TcpSyncStockService
nesse sistema operacional.
Depois de instalar o exemplo
TcpSyncStockService
, faça o seguinte:Abra o
TcpSyncStockService
no Visual Studio. (O exemplo TcpSyncStockService é instalado com o WSE 3.0. Ele não faz parte do código deste exemplo.)Defina o projeto StockService como o projeto inicialização.
Abra StockService.cs no projeto StockService e comente o atributo [Policy] na classe
StockService
. Isso desabilita a segurança do exemplo. Embora o WCF possa interoperar com pontos de extremidade seguros do WSE 3.0, a segurança está desabilitada para manter esse exemplo focado no transporte de TCP personalizado.pressione F5 para iniciar o
TcpSyncStockService
. O serviço inicia em uma nova janela de console.Abra este exemplo de transporte TCP no Visual Studio.
Atualize a variável "hostname" em TestCode.cs para corresponder ao nome do computador que executa o
TcpSyncStockService
.pressione F5 para iniciar o exemplo de transporte de TCP.
O cliente de teste de transporte de TCP é iniciado em um novo console. O cliente solicita cotações de ações ao serviço e exibe os resultados na janela do console.