Partilhar via


Noções básicas sobre alterações de estado

Este tópico discute os estados e transições que os canais têm, os tipos usados para estruturar os estados do canal e como implementá-los.

Máquinas e canais de estado

Objetos que lidam com comunicação, por exemplo, soquetes, geralmente apresentam uma máquina de estado cujas transições de estado estão relacionadas à alocação de recursos de rede, fazendo ou aceitando conexões, fechando conexões e terminando a comunicação. A máquina de estado do canal fornece um modelo uniforme dos estados de um objeto de comunicação que abstrai a implementação subjacente desse objeto. A ICommunicationObject interface fornece um conjunto de estados, métodos de transição de estado e eventos de transição de estado. Todos os canais, fábricas de canais e ouvintes de canais implementam a máquina de estado do canal.

Os eventos Fechado, Fechamento, Falhado, Aberto e Abertura sinalizam um observador externo após a ocorrência de uma transição de estado.

Os métodos Abort, Close e Open (e seus equivalentes assíncronos) causam transições de estado.

A propriedade state retorna o estado atual conforme definido por CommunicationState:

ICommunicationObject, CommunicationObject e Estados e Transição de Estado

Um ICommunicationObject começa no estado Created onde suas várias propriedades podem ser configuradas. Uma vez no estado Aberto, o objeto é utilizável para enviar e receber mensagens, mas suas propriedades são consideradas imutáveis. Uma vez no estado Fechamento, o objeto não pode mais processar novas solicitações de envio ou recebimento, mas as solicitações existentes têm a chance de ser concluídas até que o tempo limite de Fechamento seja atingido. Se ocorrer um erro irrecuperável, o objeto transita para o estado Faulted, onde pode ser inspecionado para obter informações sobre o erro e, finalmente, fechado. Quando no estado Fechado, o objeto atingiu essencialmente o final da máquina de estado. Quando um objeto transita de um estado para o outro, ele não volta para um estado anterior.

O diagrama a seguir mostra os estados e as ICommunicationObject transições de estado. As transições de estado podem ser causadas chamando um dos três métodos: Abortar, Abrir ou Fechar. Eles também podem ser causados pela chamada de outros métodos específicos de implementação. A transição para o estado Faulted pode acontecer como resultado de erros ao abrir ou depois de ter aberto o objeto de comunicação.

Tudo ICommunicationObject começa no estado Criado. Nesse estado, um aplicativo pode configurar o objeto definindo suas propriedades. Quando um objeto está em um estado diferente de Criado, ele é considerado imutável.

Dataflow diagram of the channel state transition.
Figura 1. A máquina de estado ICommunicationObject.

Windows Communication Foundation (WCF) fornece uma classe base abstrata chamada CommunicationObject que implementa e a máquina de estado do ICommunicationObject canal. O gráfico a seguir é um diagrama de estado modificado específico para CommunicationObject. Além da ICommunicationObject máquina de estado, ela mostra o tempo em que métodos adicionais CommunicationObject são invocados.

Dataflow diagram of CommunicationObject implementation state changes. Figura 2. A implementação CommunicationObject da máquina de estado ICommunicationObject, incluindo chamadas para eventos e métodos protegidos.

Eventos ICommunicationObject

CommunicationObject expõe os cinco eventos definidos por ICommunicationObject. Esses eventos são projetados para código usando o objeto de comunicação a ser notificado de transições de estado. Como mostrado na Figura 2 acima, cada evento é disparado uma vez após a transição de estado do objeto para o estado nomeado pelo evento. Todos os cinco eventos são do EventHandler tipo que é definido como:

public delegate void EventHandler(object sender, EventArgs e);

CommunicationObject Na implementação, o remetente é o CommunicationObject próprio ou o que foi passado como o remetente para o CommunicationObject construtor (se essa sobrecarga do construtor foi usada). O parâmetro EventArgs, e, é sempre EventArgs.Empty.

Retornos de chamada de objeto derivado

Além dos cinco eventos, CommunicationObject declara oito métodos virtuais protegidos projetados para permitir que um objeto derivado seja chamado de volta antes e depois que as transições de estado ocorrerem.

Os CommunicationObject.Open métodos e CommunicationObject.Close têm três retornos de chamada associados a cada um deles. Por exemplo, correspondente a CommunicationObject.OpenCommunicationObject.OnOpening, CommunicationObject.OnOpene CommunicationObject.OnOpened. Associados estão CommunicationObject.Close o CommunicationObject.OnClose, CommunicationObject.OnClosing, e CommunicationObject.OnClosed métodos.

Da mesma forma, o CommunicationObject.Abort método tem um correspondente CommunicationObject.OnAbort.

Enquanto CommunicationObject.OnOpen, CommunicationObject.OnClosee CommunicationObject.OnAbort não têm implementação padrão, os outros retornos de chamada têm uma implementação padrão que é necessária para a correção da máquina de estado. Se você substituir esses métodos, certifique-se de chamar a implementação base ou substituí-la corretamente.

CommunicationObject.OnOpening, CommunicationObject.OnClosing e disparar CommunicationObject.OnFaulted o correspondente CommunicationObject.Opening, CommunicationObject.Closing e CommunicationObject.Faulted eventos. CommunicationObject.OnOpened e CommunicationObject.OnClosed defina o estado do objeto como Aberto e Fechado, respectivamente, em seguida, dispare os eventos e correspondentes CommunicationObject.OpenedCommunicationObject.Closed .

Métodos de transição de estado

CommunicationObject fornece implementações de Abort, Close e Open. Ele também fornece um método Fault que causa uma transição de estado para o estado Faulted. A Figura 2 mostra a ICommunicationObject máquina de estado com cada transição rotulada pelo método que a causa (transições não rotuladas acontecem dentro da implementação do método que causou a última transição rotulada).

Nota

Todas as CommunicationObject implementações de gets/sets de estado de comunicação são sincronizadas com threads.

Construtor

CommunicationObject fornece três construtores, todos os quais deixam o objeto no estado Created. Os construtores são definidos como:

O primeiro construtor é um construtor sem parâmetros que delega à sobrecarga do construtor que leva um objeto:

protected CommunicationObject() : this(new object()) { … }

O construtor que usa um objeto usa esse parâmetro como o objeto a ser bloqueado ao sincronizar o acesso ao estado do objeto de comunicação:

protected CommunicationObject(object mutex) { … }

Finalmente, um terceiro construtor usa um parâmetro adicional que é usado como o argumento sender quando ICommunicationObject os eventos são acionados.

protected CommunicationObject(object mutex, object eventSender) { … }

Os dois construtores anteriores definiram o remetente para isso.

Método aberto

Pré-condição: Estado é criado.

Pós-condição: O estado está aberto ou com defeito. Pode abrir uma exceção.

O método Open() tentará abrir o objeto de comunicação e definir o estado como Opened. Se encontrar um erro, ele definirá o estado como Faulted.

O método primeiro verifica se o estado atual é Created. Se o estado atual for Abrindo ou Aberto, ele lançará um InvalidOperationExceptionarquivo . Se o estado atual for Closing ou Closed, ele lançará um CommunicationObjectAbortedException se o objeto tiver sido encerrado e ObjectDisposedException de outra forma. Se o estado atual for Faulted, ele lançará um CommunicationObjectFaultedExceptionarquivo .

Em seguida, define o estado como Opening e chama OnOpening() (que gera o evento Opening), OnOpen() e OnOpened() nessa ordem. OnOpened() define o estado como Opened e gera o evento Opened. Se qualquer um deles lançar uma exceção, Open()chama Fault() e deixa a exceção borbulhar. O diagrama a seguir mostra o processo Open com mais detalhes.

Dataflow diagram of ICommunicationObject.Open state changes.
Substitua o método OnOpen para implementar lógica aberta personalizada, como abrir um objeto de comunicação interna.

Método Close

Pré-condição: Nenhuma.

Pós-condição: Estado fechado. Pode abrir uma exceção.

O método Close() pode ser chamado em qualquer estado. Ele tenta fechar o objeto normalmente. Se for encontrado um erro, ele encerra o objeto. O método não faz nada se o estado atual for Closing ou Closed. Caso contrário, ele define o estado como Fechamento. Se o estado original foi Criado, Abrindo ou Com Falha, ele chamará Abort() (consulte o diagrama a seguir). Se o estado original foi Opened, ele chamará OnClosing() (que gera o evento Closing ), OnClose() e OnClosed() nessa ordem. Se qualquer um deles lançar uma exceção, Close()chama Abort() e deixa a exceção borbulhar. OnClosed() define o estado como Closed e gera o evento Closed. O diagrama a seguir mostra o processo Close com mais detalhes.

Dataflow diagram of ICommunicationObject.Close state changes.
Substitua o método OnClose para implementar a lógica de fechamento personalizada, como fechar um objeto de comunicação interno. Toda a lógica de fechamento normal que pode bloquear por um longo tempo (por exemplo, esperando que o outro lado responda) deve ser implementada em OnClose() porque leva um parâmetro de tempo limite e porque não é chamado como parte de Abort().

Abortar

Pré-condição: Nenhuma.
Pós-condição: Estado fechado. Pode abrir uma exceção.

O método Abort() não faz nada se o estado atual for Closed ou se o objeto tiver sido encerrado antes (por exemplo, possivelmente por ter Abort() em execução em outro thread). Caso contrário, ele define o estado como Closing e chama OnClosing() (que gera o evento Closing ), OnAbort() e OnClosed() nessa ordem (não chama OnClose porque o objeto está sendo encerrado, não fechado). OnClosed() define o estado como Closed e gera o evento Closed. Se algum deles lançar uma exceção, ele será relançado para o chamador de Abortar. Implementações de OnClosing(), OnClosed() e OnAbort() não devem bloquear (por exemplo, na entrada/saída). O diagrama a seguir mostra o processo Abort com mais detalhes.

Dataflow diagram of ICommunicationObject.Abort state changes.
Substitua o método OnAbort para implementar a lógica de terminação personalizada, como encerrar um objeto de comunicação interno.

Falha

O método Fault é específico CommunicationObject e não faz parte da ICommunicationObject interface. Está incluído aqui para ser completo.

Pré-condição: Nenhuma.

Pós-condição: O estado está com defeito. Pode abrir uma exceção.

O método Fault() não faz nada se o estado atual for Faulted ou Closed. Caso contrário, ele define o estado como Faulted e chama OnFaulted(), o que gera o evento Faulted. Se OnFaulted lançar uma exceção, ela será relançada.

Métodos ThrowIfXxx

CommunicationObject tem três métodos protegidos que podem ser usados para lançar exceções se o objeto estiver em um estado específico.

ThrowIfDisposed lança uma exceção se o estado for Fechamento, Fechado ou Falhado.

ThrowIfDisposedOrImmutable lança uma exceção se o estado não for Criado.

ThrowIfDisposedOrNotOpen lança uma exceção se o estado não for Aberto.

As exceções lançadas dependem do Estado. A tabela a seguir mostra os diferentes estados e o tipo de exceção correspondente lançado chamando um ThrowIfXxx que lança nesse estado.

Estado Abort foi chamado? Exceção
Criado N/A System.InvalidOperationException
Abrir N/A System.InvalidOperationException
Aberto N/A System.InvalidOperationException
Fechar Sim System.ServiceModel.CommunicationObjectAbortedException
Fechar Não System.ObjectDisposedException
Fechadas Sim System.ServiceModel.CommunicationObjectAbortedException no caso de um objeto ter sido fechado por uma chamada anterior e explícita de Abort. Se você chamar Close no objeto, então um System.ObjectDisposedException é lançado.
Fechadas Não System.ObjectDisposedException
Com defeito N/A System.ServiceModel.CommunicationObjectFaultedException

Tempos limite

Vários dos métodos que discutimos usam parâmetros de tempo limite. Estes são Close, Open (certas sobrecargas e versões assíncronas), OnClose e OnOpen. Esses métodos são projetados para permitir operações longas (por exemplo, bloquear a entrada/saída enquanto fecha normalmente uma conexão) para que o parâmetro de tempo limite indique quanto tempo essas operações podem levar antes de serem interrompidas. As implementações de qualquer um desses métodos devem usar o valor de tempo limite fornecido para garantir que ele retorne ao chamador dentro desse tempo limite. As implementações de outros métodos que não levam um tempo limite não são projetadas para operações longas e não devem bloquear a entrada/saída.

A exceção são as sobrecargas Open() e Close() que não levam um tempo limite. Eles usam um valor de tempo limite padrão fornecido pela classe derivada. CommunicationObject expõe duas propriedades abstratas protegidas nomeadas DefaultCloseTimeout e DefaultOpenTimeout definidas como:

protected abstract TimeSpan DefaultCloseTimeout { get; }

protected abstract TimeSpan DefaultOpenTimeout { get; }

Uma classe derivada implementa essas propriedades para fornecer o tempo limite padrão para as sobrecargas Open() e Close() que não tomam um valor de tempo limite. Em seguida, as implementações Open() e Close() delegam à sobrecarga que leva um tempo limite passando-lhe o valor de tempo limite padrão, por exemplo:

public void Open()

{

this.Open(this.DefaultOpenTimeout);

}

IDefaultCommunicationTimeouts

Essa interface tem quatro propriedades somente leitura para fornecer valores de tempo limite padrão para abrir, enviar, receber e fechar. Cada implementação é responsável por obter os valores padrão da maneira apropriada. Como uma conveniência, ChannelFactoryBase e ChannelListenerBase padrão esses valores para 1 minuto cada.