Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В примере сжатия показано, как реализовать пользовательский кодировщик с помощью платформы Windows Communication Foundation (WCF).
Детали образца
Этот пример состоит из клиентской консольной программы (.exe), локальной программы консоли службы (.exe) и библиотеки кодировщика сообщений сжатия (.dll). Служба реализует контракт, определяющий шаблон связи с запросом и ответом. Контракт определяется интерфейсом ISampleServer , который предоставляет базовые операции эхостроирования строк (Echo и BigEcho). Клиент выполняет синхронные запросы к данной операции, и служба отвечает, повторяя сообщение обратно клиенту. Действие клиента и службы отображается в окнах консоли. Цель этого примера — показать, как написать пользовательский кодировщик и продемонстрировать влияние сжатия сообщения на провод. Вы можете добавить инструментирование в кодировщик сжатия сообщений, чтобы вычислить размер сообщения, время обработки или то и другое.
Замечание
В .NET Framework 4 автоматическая декомпрессия была включена на клиенте WCF, если сервер отправляет сжатый ответ (созданный с помощью алгоритма, например GZip или Deflate). Если служба размещена на веб-сервере IIS, то IIS может быть настроен для отправки сжатого ответа. Этот пример можно использовать, если требование заключается в том, чтобы выполнять сжатие и декомпрессию как на клиенте, так и в службе, или если служба размещена самостоятельно.
В примере показано, как создать и интегрировать пользовательский кодировщик сообщений в приложение WCF. Библиотека GZipEncoder.dll развертывается как с клиентом, так и со службой. В этом примере также демонстрируется влияние сжатия сообщений. Код в GZipEncoder.dll демонстрирует следующее:
Создание кастомного кодировщика и фабрики кодировщиков.
Разработка элемента привязки для пользовательского кодировщика.
Использование пользовательской конфигурации привязки для интеграции собственных элементов привязки.
Разработка настраиваемого обработчика конфигурации, позволяющего настроить файл конфигурации для пользовательского элемента связывания.
Как указано ранее, существует несколько слоев, реализованных в пользовательском кодировщике. Чтобы лучше проиллюстрировать связь между каждым из этих уровней, упрощенный порядок событий для запуска службы находится в следующем списке:
Запускается сервер.
Сведения о конфигурации считываются.
Конфигурация службы регистрирует настраиваемый обработчик конфигурации.
Хост службы создается и открывается.
Элемент пользовательской конфигурации создает и возвращает элемент пользовательской привязки.
Элемент пользовательской привязки создает и возвращает фабрику для кодирования сообщений.
Получено сообщение.
Фабрика кодировщиков сообщений возвращает кодировщик для чтения сообщения и записи ответа.
Уровень кодировщика реализуется как фабрика классов. Для пользовательского кодировщика публично должна быть доступна только фабрика классов кодировщика. Объект фабрики возвращается элементом привязки при создании объекта ServiceHost или ChannelFactory<TChannel>. Кодировщики сообщений могут работать в буферизованном или потоковом режиме. В этом примере демонстрируется как буферный режим, так и режим потоковой передачи.
Для каждого режима есть сопровождающий ReadMessage и WriteMessage метод в абстрактном MessageEncoder классе. Большинство работ по кодировке происходит в этих методах. Пример упаковывает существующие кодировщики текстовых и двоичных сообщений. Это позволяет образцу делегировать чтение и запись проводного представления сообщений внутреннему кодировщику и позволяет кодировщику сжатия сжимать или распаковывать результаты. Так как для кодирования сообщений нет конвейера, это единственная модель для использования нескольких кодировщиков в WCF. После распаковки сообщения результирующее сообщение передается в стек для обработки стеком каналов. Во время сжатия полученное сжатое сообщение записывается непосредственно в предоставленный поток.
В этом примере используются вспомогательные методы (CompressBuffer и DecompressBuffer) для преобразования из буферов в потоки для использования GZipStream класса.
Буферные ReadMessage и WriteMessage классы используют BufferManager класс. Кодировщик доступен только через фабрику кодировщиков. Абстрактный MessageEncoderFactory класс предоставляет свойство с именем Encoder для доступа к текущему кодировщику и методу с именем CreateSessionEncoder для создания кодировщика, поддерживающего сеансы. Такой кодировщик можно использовать в сценарии, где канал поддерживает сеансы, упорядочен и является надежным. Этот сценарий позволяет оптимизацию на каждом сеансе данных, записываемых в линию соединения. Если это не нужно, базовый метод не должен быть перегружен. Свойство Encoder предоставляет механизм доступа к кодировщику без сеанса, а реализация CreateSessionEncoder метода по умолчанию возвращает значение свойства. Поскольку пример оборачивает существующий кодировщик для обеспечения сжатия, реализация принимает MessageEncoderFactory, представляющий внутреннюю фабрику кодировщика MessageEncoderFactory.
Теперь, когда определены кодировщик и фабрика кодировщиков, их можно использовать с клиентом и службой WCF. Однако эти кодировщики необходимо добавить в стек каналов. Вы можете наследовать классы от ServiceHost и ChannelFactory<TChannel>, а также переопределить методы OnInitialize, чтобы вручную добавить эту фабрику кодировщика. Кроме того, можно открыть доступ к фабрике кодировщика с помощью пользовательского элемента привязки.
Чтобы создать новый пользовательский элемент привязки, наследуйте класс из BindingElement класса. Однако существует несколько типов элементов привязки. Чтобы убедиться, что настраиваемый элемент привязки распознается как элемент привязки кодирования сообщений, необходимо также реализовать элемент привязки MessageEncodingBindingElement. Этот элемент MessageEncodingBindingElement предоставляет метод для создания новой фабрики кодировщика сообщений (CreateMessageEncoderFactory), который реализован для возврата экземпляра соответствующей фабрики кодировщика сообщений. Кроме того, MessageEncodingBindingElement имеет свойство, указывающее версию адресации. Так как этот пример упаковывает существующие кодировщики, пример реализации также упаковывает существующие элементы привязки кодировщика и принимает внутренний элемент привязки кодировщика в качестве параметра конструктору и предоставляет его через свойство. В следующем примере кода показана реализация GZipMessageEncodingBindingElement класса.
public sealed class GZipMessageEncodingBindingElement
: MessageEncodingBindingElement //BindingElement
, IPolicyExportExtension
{
//We use an inner binding element to store information
//required for the inner encoder.
MessageEncodingBindingElement innerBindingElement;
//By default, use the default text encoder as the inner encoder.
public GZipMessageEncodingBindingElement()
: this(new TextMessageEncodingBindingElement()) { }
public GZipMessageEncodingBindingElement(MessageEncodingBindingElement messageEncoderBindingElement)
{
this.innerBindingElement = messageEncoderBindingElement;
}
public MessageEncodingBindingElement InnerMessageEncodingBindingElement
{
get { return innerBindingElement; }
set { innerBindingElement = value; }
}
//Main entry point into the encoder binding element.
// Called by WCF to get the factory that creates the
//message encoder.
public override MessageEncoderFactory CreateMessageEncoderFactory()
{
return new
GZipMessageEncoderFactory(innerBindingElement.CreateMessageEncoderFactory());
}
public override MessageVersion MessageVersion
{
get { return innerBindingElement.MessageVersion; }
set { innerBindingElement.MessageVersion = value; }
}
public override BindingElement Clone()
{
return new
GZipMessageEncodingBindingElement(this.innerBindingElement);
}
public override T GetProperty<T>(BindingContext context)
{
if (typeof(T) == typeof(XmlDictionaryReaderQuotas))
{
return innerBindingElement.GetProperty<T>(context);
}
else
{
return base.GetProperty<T>(context);
}
}
public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
{
if (context == null)
throw new ArgumentNullException("context");
context.BindingParameters.Add(this);
return context.BuildInnerChannelFactory<TChannel>();
}
public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
{
if (context == null)
throw new ArgumentNullException("context");
context.BindingParameters.Add(this);
return context.BuildInnerChannelListener<TChannel>();
}
public override bool CanBuildChannelListener<TChannel>(BindingContext context)
{
if (context == null)
throw new ArgumentNullException("context");
context.BindingParameters.Add(this);
return context.CanBuildInnerChannelListener<TChannel>();
}
void IPolicyExportExtension.ExportPolicy(MetadataExporter exporter, PolicyConversionContext policyContext)
{
if (policyContext == null)
{
throw new ArgumentNullException("policyContext");
}
XmlDocument document = new XmlDocument();
policyContext.GetBindingAssertions().Add(document.CreateElement(
GZipMessageEncodingPolicyConstants.GZipEncodingPrefix,
GZipMessageEncodingPolicyConstants.GZipEncodingName,
GZipMessageEncodingPolicyConstants.GZipEncodingNamespace));
}
}
Обратите внимание, что GZipMessageEncodingBindingElement класс реализует IPolicyExportExtension интерфейс, чтобы этот элемент привязки можно экспортировать как политику в метаданных, как показано в следующем примере.
<wsp:Policy wsu:Id="BufferedHttpSampleServer_ISampleServer_policy">
<wsp:ExactlyOne>
<wsp:All>
<gzip:text xmlns:gzip=
"http://schemas.microsoft.com/ws/06/2004/mspolicy/netgzip1" />
<wsaw:UsingAddressing />
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
Класс GZipMessageEncodingBindingElementImporter реализует IPolicyImportExtension интерфейс, для этого класса импортируется политика GZipMessageEncodingBindingElement. средство Svcutil.exe можно использовать для импорта политик в файл конфигурации, для обработки GZipMessageEncodingBindingElement, в Svcutil.exe.configследует добавить следующее.
<configuration>
<system.serviceModel>
<extensions>
<bindingElementExtensions>
<add name="gzipMessageEncoding"
type=
"Microsoft.ServiceModel.Samples.GZipMessageEncodingElement, GZipEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
<client>
<metadata>
<policyImporters>
<remove type=
"System.ServiceModel.Channels.MessageEncodingBindingElementImporter, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<extension type=
"Microsoft.ServiceModel.Samples.GZipMessageEncodingBindingElementImporter, GZipEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</policyImporters>
</metadata>
</client>
</system.serviceModel>
</configuration>
Теперь, когда для кодировщика сжатия есть соответствующий элемент привязки, его можно программно подключить к службе или клиенту, создав новый объект пользовательской привязки и добавив к нему элемент пользовательской привязки, как показано в следующем примере кода.
ICollection<BindingElement> bindingElements = new List<BindingElement>();
HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();
GZipMessageEncodingBindingElement compBindingElement = new GZipMessageEncodingBindingElement ();
bindingElements.Add(compBindingElement);
bindingElements.Add(httpBindingElement);
CustomBinding binding = new CustomBinding(bindingElements);
binding.Name = "SampleBinding";
binding.Namespace = "http://tempuri.org/bindings";
Хотя это может быть достаточно для большинства пользовательских сценариев, поддержка конфигурации файлов имеет решающее значение, если служба должна размещаться в Интернете. Для поддержки сценария, размещенного в Интернете, необходимо разработать настраиваемый обработчик конфигурации, чтобы разрешить настройку пользовательского элемента привязки в файле.
Вы можете создать обработчик конфигурации для элемента привязки на вершине системы конфигурации. Обработчик конфигурации для элемента привязки должен быть производным от BindingElementExtensionElement класса.
BindingElementExtensionElement.BindingElementType сообщает системе конфигурации, какой тип элемента привязки будет создан для этого раздела. Все аспекты BindingElement, которые можно настроить, должны быть представлены в виде свойств в производном BindingElementExtensionElement классе. Элемент ConfigurationPropertyAttribute помогает сопоставить атрибуты элемента конфигурации со свойствами и устанавливает значения по умолчанию, если атрибуты отсутствуют. После загрузки и применения значений из конфигурации к свойствам BindingElementExtensionElement.CreateBindingElement вызывается метод, который преобразует свойства в конкретный экземпляр элемента привязки. Метод BindingElementExtensionElement.ApplyConfiguration используется для преобразования свойств BindingElementExtensionElement производного класса в значения, заданные в созданном элементе привязки.
В следующем примере кода показана реализация GZipMessageEncodingElement.
public class GZipMessageEncodingElement : BindingElementExtensionElement
{
public GZipMessageEncodingElement()
{
}
//Called by the WCF to discover the type of binding element this
//config section enables
public override Type BindingElementType
{
get { return typeof(GZipMessageEncodingBindingElement); }
}
//The only property we need to configure for our binding element is
//the type of inner encoder to use. Here, we support text and
//binary.
[ConfigurationProperty("innerMessageEncoding",
DefaultValue = "textMessageEncoding")]
public string InnerMessageEncoding
{
get { return (string)base["innerMessageEncoding"]; }
set { base["innerMessageEncoding"] = value; }
}
//Called by the WCF to apply the configuration settings (the
//property above) to the binding element
public override void ApplyConfiguration(BindingElement bindingElement)
{
GZipMessageEncodingBindingElement binding =
(GZipMessageEncodingBindingElement)bindingElement;
PropertyInformationCollection propertyInfo =
this.ElementInformation.Properties;
if (propertyInfo["innerMessageEncoding"].ValueOrigin !=
PropertyValueOrigin.Default)
{
switch (this.InnerMessageEncoding)
{
case "textMessageEncoding":
binding.InnerMessageEncodingBindingElement =
new TextMessageEncodingBindingElement();
break;
case "binaryMessageEncoding":
binding.InnerMessageEncodingBindingElement =
new BinaryMessageEncodingBindingElement();
break;
}
}
}
//Called by the WCF to create the binding element
protected override BindingElement CreateBindingElement()
{
GZipMessageEncodingBindingElement bindingElement =
new GZipMessageEncodingBindingElement();
this.ApplyConfiguration(bindingElement);
return bindingElement;
}
}
Этот обработчик конфигурации соответствует следующему представлению в App.config или Web.config для службы или клиента.
<gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />
Чтобы использовать этот обработчик конфигурации, его необходимо зарегистрировать в элементе <system.serviceModel> , как показано в следующем примере конфигурации.
<extensions>
<bindingElementExtensions>
<add
name="gzipMessageEncoding"
type=
"Microsoft.ServiceModel.Samples.GZipMessageEncodingElement,
GZipEncoder, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
При запуске сервера запросы и ответы операции отображаются в окне консоли. Нажмите клавишу ВВОД в окне, чтобы завершить работу сервера.
Press Enter key to Exit.
Server Echo(string input) called:
Client message: Simple hello
Server BigEcho(string[] input) called:
64 client messages
При запуске клиента запросы и ответы операции отображаются в окне консоли. Нажмите клавишу ВВОД в окне клиента, чтобы завершить работу клиента.
Calling Echo(string):
Server responds: Simple hello Simple hello
Calling BigEcho(string[]):
Server responds: Hello 0
Press <ENTER> to terminate client.
Настройка, сборка и запуск примера
Установите ASP.NET 4.0 с помощью следующей команды:
%windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enableУбедитесь, что вы выполнили процедуру настройки One-Time для образцов Windows Communication Foundation.
Чтобы создать решение, следуйте инструкциям по созданию примеров Windows Communication Foundation.
Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в запуска примеров Windows Communication Foundation.