Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом разделе рассматриваются состояния и переходы, которые имеют каналы, типы, используемые для структуры состояний каналов, и способы их реализации.
Автоматы конечных состояний и каналы
Объекты, которые имеют дело с обменом данными, например сокеты, обычно представляют собой компьютер с состоянием, переходы состояния которого связаны с выделением сетевых ресурсов, принятием или приемом подключений, закрытием подключений и завершением связи. Компьютер состояния канала предоставляет единую модель состояний объекта связи, абстрагирует базовую реализацию этого объекта. Интерфейс ICommunicationObject предоставляет набор состояний, методов перехода состояния и событий перехода состояния. Все каналы, фабрики каналов и прослушиватели каналов реализуют компьютер состояния канала.
События Закрытые, Закрывающие, Неисправные, Открытые и Открывающие сигнализируют внешнему наблюдателю после перехода состояния.
Методы Abort, Close и Open (и их асинхронные эквиваленты) вызывают переходы состояния.
Свойство состояния возвращает текущее состояние, определённое CommunicationState.
ICommunicationObject, CommunicationObject и состояния и переходы состояний
Компонент ICommunicationObject входит в состояние «Создан», где можно настроить его различные свойства. После открытия объект доступен для отправки и получения сообщений, но его свойства считаются неизменяемыми. После закрытия объект больше не может обрабатывать новые запросы на отправку или получение, но существующие запросы могут завершиться до тех пор, пока не будет достигнуто время ожидания закрытия. Если возникает неустранимая ошибка, объект переходит в состояние сбоя, где его можно проверить для получения сведений об ошибке и в конечном итоге закрыт. Когда в закрытом состоянии объект по сути достиг конца автомата состояний. После перехода объекта из одного состояния в следующее, он не возвращается к предыдущему состоянию.
На следующей ICommunicationObject диаграмме показаны состояния и переходы состояний. Переходы состояния могут быть вызваны вызовом одного из трех методов: Abort, Open или Close. Они также могут быть вызваны вызовом других методов, специфичных для реализации. Переход в состояние сбоя может произойти в результате ошибок при открытии или после открытия объекта связи.
Каждый ICommunicationObject начинается в состоянии "Создано". В этом состоянии приложение может настроить объект, задав его свойства. После того как объект находится в состоянии, отличном от Создания, он считается неизменяемым.
Рис. 1. Машина состояний ICommunicationObject.
Windows Communication Foundation (WCF) предоставляет абстрактный базовый класс CommunicationObject, который реализует ICommunicationObject и машину состояний канала. На следующем рисунке представлена измененная схема состояния, относящаяся к CommunicationObject. ICommunicationObject Помимо компьютера состояния, он показывает время вызова дополнительных CommunicationObject методов.
Рис. 2. Реализация устройства состояния ICommunicationObject, включая вызовы событий и защищенных методов.
События ICommunicationObject
CommunicationObject предоставляет пять событий, определенных ICommunicationObject. Эти события предназначены для того, чтобы код использовал объект связи для уведомления о переходах состояния. Как показано на рисунке 2 выше, каждое событие запускается один раз после перехода состояния объекта в состояние с именем события. Все пять событий относятся к типу EventHandler
, который определяется следующим образом:
public delegate void EventHandler(object sender, EventArgs e);
В данной реализации отправителем является либо сам CommunicationObject, либо то, что было передано в качестве отправителя конструктору CommunicationObject (если была использована эта перегрузка конструктора). Параметр EventArgs, e
, всегда EventArgs.Empty
.
Обратные вызовы производных объектов
Помимо пяти событий, CommunicationObject объявляет восемь защищенных виртуальных методов, предназначенных для того, чтобы вызывать производный объект до и после осуществления переходов состояний.
Методы CommunicationObject.Open и CommunicationObject.Close имеют три таких обратных вызова, связанных с каждым из них. Например, соответствуют CommunicationObject.Open, CommunicationObject.OnOpening, CommunicationObject.OnOpen и CommunicationObject.OnOpened. Связанные с CommunicationObject.Close методы: CommunicationObject.OnClose, CommunicationObject.OnClosing и CommunicationObject.OnClosed.
Аналогичным образом, метод CommunicationObject.Abort имеет соответствующий CommunicationObject.OnAbort.
Хотя CommunicationObject.OnOpen, CommunicationObject.OnCloseи CommunicationObject.OnAbort нет реализации по умолчанию, другие обратные вызовы имеют реализацию по умолчанию, которая необходима для правильности компьютера состояния. Если вы переопределяете эти методы, обязательно либо вызовите базовую реализацию, либо корректно замените её.
CommunicationObject.OnOpening, CommunicationObject.OnClosing и CommunicationObject.OnFaulted вызовут соответствующие события CommunicationObject.Opening, CommunicationObject.Closing и CommunicationObject.Faulted. CommunicationObject.OnOpened и CommunicationObject.OnClosed устанавливают для состояния объекта значения "Открытый" и "Закрытый", после чего срабатывают соответствующие события CommunicationObject.Opened и CommunicationObject.Closed.
Методы перехода состояния
CommunicationObject предоставляет реализации Abort, Close и Open. Он также предоставляет метод сбоя, который приводит к переходу состояния в состояние сбоя. На рисунке 2 показана ICommunicationObject машина состояния с каждым переходом, помеченным методом, который вызывает его (неназначенные переходы происходят внутри реализации метода, вызвавшего последний помеченный переход).
Замечание
Все реализации состояния связи CommunicationObject потокосинхронизированно получают и задаются.
Конструктор
CommunicationObject предоставляет три конструктора, все из которых покидают объект в состоянии "Создано". Конструкторы определяются следующим образом:
Первый конструктор — это конструктор без параметров, который делегирует выполнение своей работы другому перегруженному конструктору, принимающему объект.
protected CommunicationObject() : this(new object()) { … }
Конструктор, принимающий объект, использует этот параметр в качестве объекта для блокировки при синхронизации доступа к состоянию объекта связи:
protected CommunicationObject(object mutex) { … }
Наконец, третий конструктор принимает дополнительный параметр, который используется в качестве аргумента отправителя, когда события ICommunicationObject срабатывают.
protected CommunicationObject(object mutex, object eventSender) { … }
Предыдущие два конструктора задают отправителю значение этого параметра.
Метод Open
Предварительные условия: создается состояние.
После условия: состояние открыто или неисправно. Может вызвать исключение.
Метод Open() попытается открыть объект связи и задать состояние Open. Если возникает ошибка, состояние будет установлено как "Ошибочное".
Метод сначала проверяет, что текущее состояние создано. Если текущее состояние находится в процессе открытия или открыто, это вызывает InvalidOperationException. Если текущее состояние является закрытием или закрытым, он выбрасывает исключение CommunicationObjectAbortedException, если объект был завершён, и ObjectDisposedException в противном случае. Если текущее состояние является ошибочным, генерируется CommunicationObjectFaultedException.
Затем он задает состояние "Открытие" и вызывает OnOpening(), которое вызывает событие "Открытие", OnOpen() и OnOpened() в этом порядке. OnOpened() задает состояние "Открыто" и вызывает событие "Открыто". Если любой из этих вызовов вызывает исключение, Open() вызывает Fault() и позволяет исключению всплыть. На следующей схеме более подробно показан процесс Open.
Переопределите метод OnOpen, чтобы реализовать пользовательскую логику открытия, например открытие внутреннего объекта связи.
Метод Закрытия
Предварительные условия: Нет.
Постусловие: состояние закрыто. Может вызвать исключение.
Метод Close() можно вызывать в любом состоянии. Обычно он пытается закрыть объект. Если возникает ошибка, объект прекращается. Метод ничего не делает, если текущее состояние на стадии закрытия или закрыто. В противном случае оно задает состояние "Закрытие". Если исходное состояние было создано, открытие или ошибка, вызывает abort() (см. следующую схему). Если исходное состояние было 'Открыто', он вызывает OnClosing() (который вызывает событие закрытия), OnClose() и OnClosed() в этом порядке. Если любое из этих исключений происходит, Close() вызывает Abort() и исключение продолжает распространяться вверх по стеку вызовов. OnClosed() задает состояние "Закрыто" и вызывает событие "Закрыто". На следующей схеме более подробно показан процесс закрытия.
Переопределите метод OnClose для реализации пользовательской логики закрытия, например закрытия внутреннего объекта связи. Все корректное закрытие логики, которая может блокироваться в течение длительного времени (например, ожидание ответа другой стороны), должна быть реализована в OnClose(), так как он требует параметр времени ожидания и поскольку не вызывается как часть Abort().
Отмена
Предварительные условия: Нет.
Постусловие: состояние закрыто. Может вызвать исключение.
Метод Abort() ничего не делает, если текущий статус — Завершено или если объект был завершен раньше (например, возможно, за счет выполнения метода Abort() на другом потоке). В противном случае оно задает состояние "Закрытие" и вызывает OnClosing(), которое вызывает событие "Закрытие", OnAbort() и OnClosed() в этом порядке (не вызывает OnClose, так как объект завершается, а не закрывается). OnClosed() задает состояние "Закрыто" и вызывает событие "Закрыто". Если при выполнении любого из них произойдет исключение, оно повторно выбрасывается вызывающему Abort. Реализации OnClosing(), OnClosed() и OnAbort() не должны зависать (например, на операциях ввода/вывода). На следующей схеме более подробно показан процесс прерывания.
Переопределите метод OnAbort для реализации пользовательской логики завершения, например завершения внутреннего объекта связи.
Неисправность
Метод сбоя относится к CommunicationObject интерфейсу и не является частью ICommunicationObject интерфейса. Она включена здесь для полноты.
Предварительные условия: Нет.
Постусловие: состояние аварийное. Может вызвать исключение.
Метод Fault() ничего не делает, если текущее состояние является состоянием сбоя или закрыто. В противном случае оно устанавливает состояние "Сбой" и выполняет вызов OnFaulted(), что вызывает событие "Сбой". Если OnFaulted выбрасывает исключение, оно выбрасывается повторно.
Методы ThrowIfXxx
CommunicationObject имеет три защищенных метода, которые можно использовать для создания исключений, если объект находится в определенном состоянии.
ThrowIfDisposed вызывает исключение, если состояние закрыто, закрыто или неисправно.
ThrowIfDisposedOrImmutable создает исключение, если состояние не создано.
ThrowIfDisposedOrNotOpen создает исключение, если состояние не открыто.
Выбрасываемые исключения зависят от состояния. В следующей таблице показаны различные состояния и соответствующий тип исключения, вызванный вызовом ThrowIfXxxx, который вызывает это состояние.
Государство | Вызывается ли прерывание? | Исключение |
---|---|---|
Создано | Не применимо | System.InvalidOperationException |
Открытие | Не применимо | System.InvalidOperationException |
Прочтено | Не применимо | System.InvalidOperationException |
Закрытие | Да | System.ServiceModel.CommunicationObjectAbortedException |
Закрытие | нет | System.ObjectDisposedException |
Закрытый | Да | System.ServiceModel.CommunicationObjectAbortedException В случае, если объект был закрыт предыдущим и явным вызовом Abort. При вызове Close для объекта выбрасывается исключение System.ObjectDisposedException. |
Закрытый | нет | System.ObjectDisposedException |
С ошибкой | Не применимо | System.ServiceModel.CommunicationObjectFaultedException |
тайм-ауты
Некоторые из методов, которые мы обсуждали, принимают параметры времени ожидания. Это Close, Open (определенные перегрузки и асинхронные версии), OnClose и OnOpen. Эти методы предназначены для длительных операций (например, блокирования входных и выходных данных при корректном закрытии подключения), поэтому параметр времени ожидания указывает, как долго такие операции могут занять до прерывания. Реализации любого из этих методов должны использовать предоставленное значение таймаута, чтобы гарантировать возврат к вызывающей стороне в пределах этого времени. Реализации других методов, которые не занимают время ожидания, не предназначены для длительных операций и не должны блокировать входные и выходные данные.
Исключением являются перегрузки Open() и Close(), которые не занимают время ожидания. Они используют значение времени ожидания по умолчанию, предоставленное производным классом. CommunicationObject предоставляет два защищенных абстрактных свойства с именем DefaultCloseTimeout и DefaultOpenTimeout определены следующим образом:
protected abstract TimeSpan DefaultCloseTimeout { get; }
protected abstract TimeSpan DefaultOpenTimeout { get; }
Производный класс реализует эти свойства для предоставления времени ожидания по умолчанию для перегрузок Open() и Close(), которые не принимают значения времени ожидания. Затем реализации Open() и Close() делегирует перегрузку, которая принимает время ожидания, передавая значение времени ожидания по умолчанию, например:
public void Open()
{
this.Open(this.DefaultOpenTimeout);
}
IDefaultCommunicationTimeouts
Этот интерфейс имеет четыре свойства только для чтения для предоставления значений времени ожидания по умолчанию для открытия, отправки, получения и закрытия. Каждая реализация отвечает за получение значений по умолчанию в любом случае. Для удобства ChannelFactoryBase и ChannelListenerBase устанавливают эти значения на 1 минуту.