Compartir por


Descripción de los cambios de estado

En este tema se describen los estados y transiciones que tienen los canales, los tipos usados para estructurar los estados del canal y cómo implementarlos.

Máquinas y canales de estado

Los objetos que tratan con la comunicación, por ejemplo, sockets, suelen presentar una máquina de estado cuyas transiciones de estado se relacionan con la asignación de recursos de red, realizar o aceptar conexiones, cerrar conexiones y finalizar la comunicación. La máquina de estado del canal proporciona un modelo uniforme de los estados de un objeto de comunicación que abstrae la implementación subyacente de ese objeto. La ICommunicationObject interfaz proporciona un conjunto de estados, métodos de transición de estado y eventos de transición de estado. Todos los canales, generadores de canales y escuchas de canal implementan la máquina de estados del canal.

Los eventos Cerrado, Cerrando, Error, Abierto y Abriendo señalan un observador externo después de que se produzca una transición de estado.

Los métodos Abort, Close y Open (y sus equivalentes asincrónicos) provocan transiciones de estado.

La propiedad state devuelve el estado actual definido por CommunicationState:

ICommunicationObject, CommunicationObject y estados y transición de estado

Un ICommunicationObject comienza en el estado Creado donde se pueden configurar sus diversas propiedades. Una vez en el estado Abierto, el objeto se puede usar para enviar y recibir mensajes, pero sus propiedades se consideran inmutables. Una vez en el estado De cierre, el objeto ya no puede procesar nuevas solicitudes de envío o recepción, pero las solicitudes existentes tienen la oportunidad de completarse hasta que se alcance el tiempo de espera de cierre. Si se produce un error irrecuperable, el objeto pasa al estado Faulted donde se puede inspeccionar para obtener información sobre el error y, en última instancia, se cierra. Cuando el objeto está en el estado Cerrado, ha llegado esencialmente al final de la máquina de estados. Una vez que un objeto pasa de un estado a otro, no vuelve a un estado anterior.

En el siguiente diagrama se muestran los estados ICommunicationObject y las transiciones de estado. Las transiciones de estado se pueden producir llamando a uno de los tres métodos: "Abort", "Open" o "Close". También se pueden producir llamando a otros métodos específicos de la implementación. La transición al estado Faulted podría producirse como resultado de errores al abrir o después de haber abierto el objeto de comunicación.

Cada ICommunicationObject empieza en el estado Creado. En este estado, una aplicación puede configurar el objeto estableciendo sus propiedades. Una vez que un objeto está en un estado distinto de Creado, se considera inmutable.

Diagrama de flujo de datos de la transición de estado del canal.
Figura 1. La máquina de estados de ICommunicationObject.

Windows Communication Foundation (WCF) proporciona una clase base abstracta denominada CommunicationObject que implementa ICommunicationObject y la máquina de estado del canal. El gráfico siguiente es un diagrama de estado modificado que es específico de CommunicationObject. Además de la máquina de estados ICommunicationObject, muestra el momento en que se invocan métodos adicionales CommunicationObject.

Diagrama de flujo de datos de los cambios de estado de implementación de CommunicationObject. Figura 2. La implementación de CommunicationObject del equipo de estados ICommunicationObject incluidas las llamadas a eventos y métodos protegidos.

Eventos ICommunicationObject

CommunicationObject expone los cinco eventos definidos por ICommunicationObject. Estos eventos están diseñados para que el código, que utiliza el objeto de comunicación, reciba notificaciones de transiciones de estado. Como se muestra en la figura 2 anterior, cada evento se desencadena una vez después de que el estado del objeto pase al estado denominado por el evento. Los cinco eventos son del EventHandler tipo que se define como:

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

En la implementación CommunicationObject, el remitente es el propio CommunicationObject o lo que se pasó como remitente al constructor CommunicationObject (si se utilizó esa sobrecarga de constructor). El parámetro EventArgs, e, siempre es EventArgs.Empty.

Devoluciones de llamada de objetos derivados

Además de los cinco eventos, CommunicationObject declara ocho métodos virtuales protegidos diseñados para permitir que se vuelva a llamar a un objeto derivado antes y después de que se produzcan transiciones de estado.

Los métodos CommunicationObject.Open y CommunicationObject.Close tienen tres devoluciones de llamada de este tipo asociadas a cada uno de ellos. Por ejemplo, correspondiente a CommunicationObject.Open hay CommunicationObject.OnOpening, CommunicationObject.OnOpeny CommunicationObject.OnOpened. Asociados a CommunicationObject.Close son los CommunicationObject.OnClosemétodos , CommunicationObject.OnClosingy CommunicationObject.OnClosed .

Del mismo modo, el CommunicationObject.Abort método tiene un objeto correspondiente CommunicationObject.OnAbort.

Aunque CommunicationObject.OnOpen, CommunicationObject.OnClosey CommunicationObject.OnAbort no tienen ninguna implementación predeterminada, las otras devoluciones de llamada tienen una implementación predeterminada que es necesaria para la exactitud de la máquina de estados. Si invalida esos métodos asegúrese de llamar a la implementación base o reemplazarla correctamente.

CommunicationObject.OnOpening, CommunicationObject.OnClosing y CommunicationObject.OnFaulted disparan los correspondientes eventos CommunicationObject.Opening, CommunicationObject.Closing y CommunicationObject.Faulted. CommunicationObject.OnOpened y CommunicationObject.OnClosed establecen el estado del objeto en Abierto y Cerrado, respectivamente, y activan los eventos CommunicationObject.Opened y CommunicationObject.Closed correspondientes.

Métodos de transición de estado

CommunicationObject proporciona implementaciones de Anular, Cerrar y Abrir. También proporciona un método Error que provoca una transición de estado al estado Error. En la figura 2 se muestra la ICommunicationObject máquina de estado con cada transición etiquetada por el método que lo provoca (las transiciones sin etiqueta se producen dentro de la implementación del método que provocó la última transición etiquetada).

Nota:

Todas las implementaciones CommunicationObject de estado de comunicación obtener/establecer están sincronizadas por subproceso.

Constructor

CommunicationObject proporciona tres constructores, todos los cuales dejan el objeto en estado Creado. Los constructores se definen como:

El primero es un constructor sin parámetros que delega a la sobrecarga del constructor que toma un objeto:

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

El constructor que toma un objeto usa ese parámetro como objeto que se va a bloquear al sincronizar el acceso al estado del objeto de comunicación:

protected CommunicationObject(object mutex) { … }

Finalmente, un tercer constructor toma un parámetro adicional que se utiliza como argumento de remitente cuando se desencadenan los eventos ICommunicationObject.

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

Los dos constructores anteriores establecen el remitente en esto.

Método Open

Condición previa: se crea el estado.

Condición posterior: el estado está Abierto o tiene un Error. Podría iniciar una excepción.

El método Open() intentará abrir el objeto de comunicación y establecer el estado en Abierto. Si se produce un error, establecerá el estado en Error.

El método comprueba primero que el estado actual es Creado. Si el estado actual es Abriendo o Abierto inicia InvalidOperationException. Si el estado actual es Cerrando o Cerrado, inicia CommunicationObjectAbortedException si se ha finalizado el objeto y ObjectDisposedException. Si el estado actual es Error, inicia CommunicationObjectFaultedException.

A continuación establece el estado en Abriendo y llama a OnOpening () (que desencadena el evento Abriendo), OnOpen () y OnOpened () en ese orden. OnOpened() establece el estado en Abierto y genera el evento Open. Si alguno de estos produce una excepción, Open() llama a Fault() y permite que la excepción se propague. En el diagrama siguiente se muestra el proceso de apertura con más detalle.

Diagrama de flujo de datos de los cambios de estado ICommunicationObject.Open.
Invalide el método OnOpen para implementar lógica abierta personalizada, como abrir un objeto de comunicación interno.

Close (Método)

Condición previa: Ninguna.

Condición posterior: el estado es Cerrado. Podría iniciar una excepción.

Se puede llamar al método Close() en cualquier estado. Intenta cerrar el objeto normalmente. Si se produce un error, finaliza el objeto . El método no hace nada si el estado actual es Closing o Closed. De lo contrario establece el estado en Cerrando. Si el estado original era Creado, Abriendo o Error, llama a Abort() (vea el diagrama siguiente). Si el estado original era Abierto, llama a OnClosing() (que desencadena el evento Cerrando), OnClose() y OnClosed() en ese orden. Si alguno de estos produce una excepción, Close() llama a Abort() y permite que la excepción se propague. OnClosed() establece el estado en Cerrado y desencadena el evento Cerrado. En el diagrama siguiente se muestra el proceso de cierre con más detalle.

Diagrama de flujo de datos de los cambios de estado ICommunicationObject.Close.
Invalide el método OnClose para implementar lógica de cierre personalizada, como cerrar un objeto de comunicación interno. Toda lógica de cierre elegante que se pueda bloquear durante mucho tiempo (por ejemplo, esperando a que el otro lado responda) se debería implementar en OnClose() porque toma un parámetro de tiempo de espera y porque no se llama como parte de Abort().

Abortar

Condición previa: Ninguna.
Condición posterior: el estado es Cerrado. Podría iniciar una excepción.

El método Abort() no hace nada si el estado actual es Closed o si el objeto se ha terminado antes (por ejemplo, posiblemente haciendo que Abort() se ejecute en otro subproceso). De lo contrario establece el estado a Cerrando y llama a OnClosing() (que desencadena el evento Cerrando), OnAbort() y OnClosed() en ese orden (no llama a OnClose porque se está finalizando el objeto, no cerrado). OnClosed() establece el estado en Cerrado y desencadena el evento Cerrado. Si cualquiera de estas operaciones provoca una excepción, se reinicia el llamador de Anular. Las implementaciones de OnClosing(), OnClosed() y OnAbort() no se deberían bloquear (por ejemplo, en entrada/salida). En el diagrama siguiente se muestra el proceso de aborto con más detalle.

Diagrama de flujo de datos de los cambios de estado ICommunicationObject.Abort.
Invalide el método OnAbort para implementar lógica de finalización personalizada, como finalizar un objeto de comunicación interno.

Error

El método Fault es específico de CommunicationObject y no forma parte de la ICommunicationObject interfaz. Aquí se incluye para completar la información.

Condición previa: Ninguna.

Condición posterior: el estado es Error. Podría iniciar una excepción.

El método Fault() no hace nada si el estado actual es Faulted o Closed. De lo contrario establece el estado a Error y llama a OnFaulted(), que desencadena el evento Error. Si OnFaulted producir una excepción se reinicia.

Métodos ThrowIfXxx

CommunicationObject tiene tres métodos protegidos que se pueden usar para producir excepciones si el objeto está en un estado específico.

ThrowIfDisposed produce una excepción si el estado es Cerrando, Cerrado o Error.

ThrowIfDisposedOrImmutable produce una excepción si el estado no es Creado.

ThrowIfDisposedOrNotOpen produce una excepción si el estado no está abierto.

Las excepciones lanzadas dependen del estado. En la tabla siguiente se muestran los distintos estados y el tipo de excepción correspondiente que se lanza al llamar a una función ThrowIfXxx que provoca una excepción en ese estado.

Estado ¿Se ha llamado a Anular? Excepción
Creado No disponible System.InvalidOperationException
Abertura No disponible System.InvalidOperationException
Abierto No disponible System.InvalidOperationException
Cierre System.ServiceModel.CommunicationObjectAbortedException
Cierre No System.ObjectDisposedException
Cerrada System.ServiceModel.CommunicationObjectAbortedException en el caso de que una llamada anterior y explícita de Anular cerrara un objeto. Si llama a Cerrar en el objeto, se produce System.ObjectDisposedException.
Cerrada No System.ObjectDisposedException
Errónea No disponible System.ServiceModel.CommunicationObjectFaultedException

Tiempos de expiración

Algunos de los métodos que hemos abordado utilizan parámetros de tiempo de espera. Estas son Close, Open (ciertas sobrecargas y versiones asincrónicas), OnClose y OnOpen. Estos métodos están diseñados para permitir operaciones largas (por ejemplo, el bloqueo en la entrada y salida mientras se cierra correctamente una conexión), por lo que el parámetro de tiempo de espera indica cuánto tiempo pueden tardar dichas operaciones antes de interrumpirse. Las implementaciones de cualquiera de estos métodos deben usar el valor de tiempo de espera proporcionado para asegurarse de que vuelve al autor de la llamada dentro de ese tiempo de espera. Las implementaciones de otros métodos que no tienen un tiempo de espera no están diseñadas para las operaciones largas y no se bloquean en entrada/salida.

Las excepciones son las sobrecargas de Open() y Close() que no aceptan un tiempo de espera. Usan un valor de tiempo de espera predeterminado proporcionado por la clase derivada. CommunicationObject expone dos propiedades abstractas protegidas denominadas DefaultCloseTimeout y DefaultOpenTimeout definidas como:

protected abstract TimeSpan DefaultCloseTimeout { get; }

protected abstract TimeSpan DefaultOpenTimeout { get; }

Una clase derivada implementa estas propiedades para proporcionar el tiempo de espera predeterminado para las sobrecargas Open() y Close() que no aceptan un valor de tiempo de espera. A continuación, las implementaciones de Open() y Close() delegan a la sobrecarga que tiene un tiempo de espera que les pase el valor de tiempo de espera predeterminado, por ejemplo:

public void Open()

{

this.Open(this.DefaultOpenTimeout);

}

IDefaultCommunicationTimeouts

Esta interfaz tiene cuatro propiedades de solo lectura para proporcionar valores de tiempo de espera predeterminados para abrir, enviar, recibir y cerrar. Cada implementación es responsable de obtener los valores predeterminados de la manera adecuada. Como comodidad, ChannelFactoryBase y ChannelListenerBase estos valores predeterminados son de 1 minuto cada uno.