Sdílet prostřednictvím


Vlastní kodér zpráv: Kodér komprese

Ukázka komprese ukazuje, jak implementovat vlastní kodér pomocí platformy Windows Communication Foundation (WCF).

Ukázkové podrobnosti

Tato ukázka se skládá z programu konzoly klienta (.exe), programu konzoly místní služby (.exe) a knihovny kodéru komprese zpráv (.dll). Služba implementuje kontrakt, který definuje komunikační vzor žádosti a odpovědi. Kontrakt je definován rozhraním ISampleServer, které nabízí základní operace manipulace s řetězci (Echo a BigEcho). Klient provádí synchronní požadavky na danou operaci a služba odpoví opakováním zprávy zpět klientovi. Aktivita klienta a služby je viditelná v oknech konzoly. Záměrem této ukázky je ukázat, jak napsat vlastní kodér a předvést dopad komprese zprávy na drát. Do kodéru komprese zpráv můžete přidat instrumentaci pro výpočet velikosti zprávy, doby zpracování nebo obojího.

Poznámka:

V rozhraní .NET Framework 4 byla pro klienta WCF povolena automatická dekomprimace, pokud server odesílá komprimovanou odpověď (vytvořenou pomocí algoritmu, jako je GZip nebo Deflate). Pokud je služba hostovaná na internetovém informačním serveru (IIS), může být služba IIS nakonfigurovaná tak, aby odesílala komprimovanou odpověď. Tuto ukázku můžete použít, pokud je požadavek na kompresi a dekompresi na straně klienta i služby nebo v případě, že je služba hostovaná samostatně.

Ukázka ukazuje, jak sestavit a integrovat vlastní kodér zpráv do aplikace WCF. Knihovna GZipEncoder.dll je nasazena jak s klientem, tak se službou. Tato ukázka také ukazuje dopad komprimace zpráv. Kód v GZipEncoder.dll ukazuje následující:

  • Vytvoření vlastního kodéru a továrny na kodéry

  • Vývoj elementu vazby pro vlastní kodér

  • Použití vlastní konfigurace vazby pro integraci vlastních prvků vazby.

  • Vývoj vlastní obslužné rutiny konfigurace umožňující konfiguraci souboru vlastního elementu vazby

Jak už jsme uvedli dříve, ve vlastním kodéru je implementovaných několik vrstev. Pro lepší znázornění vztahu mezi jednotlivými vrstvami je zjednodušené pořadí událostí pro spuštění služby v následujícím seznamu:

  1. Spustí se server.

  2. Informace o konfiguraci jsou načítány.

    1. Konfigurace služby zaregistruje vlastní obslužnou rutinu konfigurace.

    2. Hostitel služby se vytvoří a otevře.

    3. Element vlastní konfigurace vytvoří a vrátí vlastní element vazby.

    4. Element vlastní vazby vytvoří a vrátí továrnu kodéru pro kódování zpráv.

  3. Byla přijata zpráva.

  4. Továrna na kodéry zpráv vrátí kodér zpráv pro čtení zprávy a pro vypsání odpovědi.

  5. Vrstva kodéru se implementuje jako objekt pro vytváření tříd. Pouze továrna tříd kodéru musí být veřejně zpřístupněna pro vlastní kodér. Objekt továrny je vrácen vazebným prvkem při vytvoření objektu ServiceHost nebo ChannelFactory<TChannel>. Kodéry zpráv můžou fungovat v režimu ukládání do vyrovnávací paměti nebo streamování. Tato ukázka ukazuje režim vyrovnávací paměti i režim streamování.

Pro každý režim existuje doprovodná ReadMessage metoda a WriteMessage metoda abstraktní MessageEncoder třídy. V těchto metodách probíhá většina práce kódování. Ukázka obaluje stávající textové a binární kodéry zpráv. To umožňuje vzorku delegovat čtení a zápis dráhové reprezentace zpráv na vnitřní kodér a umožňuje komprimačnímu kodéru komprimovat či dekomprimovat výsledky. Vzhledem k tomu, že neexistuje žádný kanál pro kódování zpráv, je to jediný model pro použití více kodérů ve WCF. Jakmile je zpráva dekomprimovaná, předá se zásobníku kanálu, aby ji zpracoval. Během komprese se výsledná komprimovaná zpráva zapíše přímo do zadaného datového proudu.

Tato ukázka používá pomocné metody (CompressBuffer a DecompressBuffer) k provedení převodu z vyrovnávacích pamětí na datové proudy pro použití GZipStream třídy.

Bufferované třídy ReadMessage a WriteMessage používají třídu BufferManager. Kodér je přístupný pouze prostřednictvím továrny kodéru. Abstraktní MessageEncoderFactory třída poskytuje vlastnost pojmenovanou Encoder pro přístup k aktuálnímu kodéru a metodu pojmenovanou CreateSessionEncoder pro vytvoření kodéru, který podporuje relace. Takový kodér lze použít ve scénáři, kdy komunikační kanál podporuje relace, je spolehlivý a správně seřazený. Tento scénář umožňuje optimalizaci v každé relaci dat zapsaných do drátu. Pokud to není žádoucí, základní metoda by neměla být přetížena. Vlastnost Encoder poskytuje mechanismus pro přístup k bezstavovému kodéru a výchozí implementace metody CreateSessionEncoder vrátí hodnotu vlastnosti. Vzhledem k tomu, že ukázka obaluje existující kodér pro zajištění komprese, MessageEncoderFactory implementace přijme MessageEncoderFactory, která představuje továrnu vnitřního kodéru.

Teď, když je definován kodér a objekt pro vytváření kodérů, je možné je použít s klientem a službou WCF. Tyto kodéry musí ale být přidány do zásobníku kanálů. Třídy můžete odvodit z tříd ServiceHost a ChannelFactory<TChannel> a přepsat metody OnInitialize, abyste mohli tuto faktoru kodéru přidat ručně. Objekt pro vytváření kodérů můžete zpřístupnit také prostřednictvím vlastního elementu vazby.

Chcete-li vytvořit nový vlastní vazební prvek, odvoďte třídu od třídy BindingElement. Existuje však několik typů vazeb prvků. Chcete-li zajistit, aby byl vlastní prvek vazby rozpoznán jako prvek vazby pro kódování zpráv, musíte také implementovat MessageEncodingBindingElement. Metoda MessageEncodingBindingElement nabízí způsob, jak vytvořit novou továrnu pro kodér zpráv (CreateMessageEncoderFactory), která je implementována tak, aby vrátila instanci odpovídající továrny pro kodér zpráv. Kromě toho má vlastnost označující MessageEncodingBindingElement verzi adresování. Vzhledem k tomu, že tato ukázka zabalí existující kodéry, ukázková implementace také zabalí existující prvky vazby kodéru a vezme element vazby vnitřního kodéru jako parametr konstruktoru a zpřístupní ho prostřednictvím vlastnosti. Následující ukázkový kód ukazuje implementaci GZipMessageEncodingBindingElement třídy.

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));
    }
}

Všimněte si, že GZipMessageEncodingBindingElement třída implementuje rozhraní, takže tento element vazby IPolicyExportExtension lze exportovat jako zásadu v metadatech, jak je znázorněno v následujícím příkladu.

<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>

Třída GZipMessageEncodingBindingElementImporter implementuje IPolicyImportExtension rozhraní, tato třída importuje zásady pro GZipMessageEncodingBindingElement. nástroj Svcutil.exe lze použít k importu zásad do konfiguračního souboru, pro zpracování GZipMessageEncodingBindingElement, do Svcutil.exe.configby se měly přidat následující informace .

<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>

Teď, když existuje odpovídající element vazby pro kodér komprese, může být programově připojen ke službě nebo klientovi vytvořením nového objektu vlastní vazby a přidáním vlastního elementu vazby do něj, jak je znázorněno v následujícím vzorovém kódu.

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";

I když to může stačit pro většinu uživatelských scénářů, podpora konfigurace souborů je důležitá, pokud je služba hostovaná na webu. Chcete-li podporovat scénář hostovaný webem, musíte vyvinout vlastní obslužnou rutinu konfigurace, aby byl vlastní element vazby možné konfigurovat v souboru.

Můžete vytvořit obslužnou rutinu konfigurace pro element vazby nad konfiguračním systémem. Obslužná rutina konfigurace pro element vazby musí být odvozena z BindingElementExtensionElement třídy. BindingElementExtensionElement.BindingElementType informuje konfigurační systém o typu prvku vazby, který se má pro tuto část vytvořit. Všechny aspekty BindingElement , které lze nastavit, by měly být vystaveny jako vlastnosti v odvozené BindingElementExtensionElement třídě. ConfigurationPropertyAttribute pomáhá při mapování atributů prvku konfigurace na vlastnosti a určení výchozích hodnot, pokud tyto atributy chybí. Po načtení hodnot z konfigurace a jejich aplikaci na vlastnosti je metoda BindingElementExtensionElement.CreateBindingElement volána, která převede vlastnosti na konkrétní instanci prvku vazby. Metoda BindingElementExtensionElement.ApplyConfiguration se používá k převodu vlastností odvozené BindingElementExtensionElement třídy na hodnoty, které mají být nastaveny na nově vytvořený binding element.

Následující vzorový kód ukazuje implementaci 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;
    }
}

Tato konfigurační obslužná rutina odpovídá následujícímu zobrazení v App.config nebo Web.config pro službu nebo klienta.

<gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />

Chcete-li použít tuto obslužnou rutinu konfigurace, musí být registrována v elementu< system.serviceModel>, jak je znázorněno v následující ukázkové konfiguraci.

<extensions>
    <bindingElementExtensions>
       <add
           name="gzipMessageEncoding"
           type=
           "Microsoft.ServiceModel.Samples.GZipMessageEncodingElement,
           GZipEncoder, Version=1.0.0.0, Culture=neutral,
           PublicKeyToken=null" />
      </bindingElementExtensions>
</extensions>

Při spuštění serveru se v okně konzoly zobrazí požadavky na operace a odpovědi. Server vypnete stisknutím klávesy ENTER v okně.

Press Enter key to Exit.

        Server Echo(string input) called:
        Client message: Simple hello

        Server BigEcho(string[] input) called:
        64 client messages

Při spuštění klienta se v okně konzoly zobrazí požadavky na operace a odpovědi. Stisknutím klávesy ENTER v okně klienta klienta ukončete klienta.

Calling Echo(string):
Server responds: Simple hello Simple hello

Calling BigEcho(string[]):
Server responds: Hello 0

Press <ENTER> to terminate client.

Jak nastavit, sestavit a spustit ukázku

  1. Pomocí následujícího příkazu nainstalujte ASP.NET 4.0:

    %windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enable
    
  2. Ujistěte se, že jste provedli instalační proceduru One-Time pro ukázky Windows Communication Foundation.

  3. Pro sestavení řešení postupujte podle pokynů v Sestavení ukázek Windows Communication Foundation.

  4. Pokud chcete spustit ukázku v konfiguraci pro jeden počítač nebo pro více počítačů, postupujte podle pokynů v Spuštění ukázek Windows Communication Foundation.