Delen via


Aangepaste berichtcoderingsprogramma: encoder voor compressie

Het compressievoorbeeld laat zien hoe u een aangepaste encoder implementeert met behulp van het WCF-platform (Windows Communication Foundation).

Voorbeelddetails

Dit voorbeeld bestaat uit een clientconsoleprogramma (.exe), een zelf-hostend serviceconsoleprogramma (.exe) en een bibliotheek voor het coderingsprogramma voor compressieberichten (.dll). De service implementeert een contract dat een communicatiepatroon aanvraagantwoord definieert. Het contract wordt gedefinieerd door de ISampleServer interface, die eenvoudige tekenreeks echobewerkingen (Echo en BigEcho) beschikbaar maakt. De client doet synchrone aanvragen voor een bepaalde bewerking en de service reageert door het bericht weer naar de client te herhalen. Client- en serviceactiviteit is zichtbaar in de consolevensters. Het doel van dit voorbeeld is om te laten zien hoe u een aangepaste encoder schrijft en de impact van compressie van een bericht op de kabel demonstreert. U kunt instrumentatie toevoegen aan de encoder voor compressieberichten om de berichtgrootte, verwerkingstijd of beide te berekenen.

Notitie

In .NET Framework 4 is automatische decompressie ingeschakeld op een WCF-client als de server een gecomprimeerd antwoord verzendt (gemaakt met een algoritme zoals GZip of Deflate). Als de service wordt gehost op internetinformatieserver (IIS), kan IIS worden geconfigureerd voor de service om een gecomprimeerd antwoord te verzenden. Dit voorbeeld kan worden gebruikt als de vereiste is om compressie en decompressie uit te voeren op zowel de client als de service of als de service zelf wordt gehost.

In het voorbeeld ziet u hoe u een aangepaste berichtcoderingsprogramma bouwt en integreert in een WCF-toepassing. De bibliotheek GZipEncoder.dll wordt geïmplementeerd met zowel de client als de service. In dit voorbeeld ziet u ook de impact van het comprimeren van berichten. De code in GZipEncoder.dll laat het volgende zien:

  • Een aangepaste encoder- en encoderfactory bouwen.

  • Het ontwikkelen van een bindingselement voor een aangepaste encoder.

  • De aangepaste bindingsconfiguratie gebruiken voor het integreren van aangepaste bindingselementen.

  • Het ontwikkelen van een aangepaste configuratie-handler om bestandsconfiguratie van een aangepast bindingselement toe te staan.

Zoals eerder is aangegeven, zijn er verschillende lagen die worden geïmplementeerd in een aangepaste encoder. Voor een beter beeld van de relatie tussen elk van deze lagen staat een vereenvoudigde volgorde van gebeurtenissen voor het opstarten van de service in de volgende lijst:

  1. De server wordt gestart.

  2. De configuratiegegevens worden gelezen.

    1. De serviceconfiguratie registreert de aangepaste configuratiehandler.

    2. De servicehost wordt gemaakt en geopend.

    3. Het aangepaste configuratie-element maakt en retourneert het aangepaste bindingselement.

    4. Het aangepaste bindingselement maakt en retourneert een factory voor berichtcoderingsprogramma's.

  3. Er wordt een bericht ontvangen.

  4. De factory van de berichtcoderingsprogramma retourneert een berichtcoderingsprogramma voor het lezen in het bericht en het schrijven van het antwoord.

  5. De encoderlaag wordt geïmplementeerd als een klassefactory. Alleen de encoderklassefactory moet openbaar worden weergegeven voor de aangepaste encoder. Het factory-object wordt geretourneerd door het bindingselement wanneer het ServiceHost of ChannelFactory<TChannel> object wordt gemaakt. Berichtcoderingsprogramma's kunnen worden uitgevoerd in een buffer- of streamingmodus. In dit voorbeeld ziet u zowel de buffermodus als de streamingmodus.

Voor elke modus is er een bijbehorende ReadMessage en WriteMessage methode in de abstracte MessageEncoder klasse. Een meerderheid van de coderingswerkzaamheden vindt plaats in deze methoden. Het voorbeeld verpakt de bestaande tekst- en binaire berichtcoderingsprogramma's. Hierdoor kan het voorbeeld het lezen en schrijven van de draadweergave van berichten delegeren aan de binnenste encoder en kan de compressiecoderingsprogramma de resultaten comprimeren of decomprimeren. Omdat er geen pijplijn is voor berichtcodering, is dit het enige model voor het gebruik van meerdere encoders in WCF. Zodra het bericht is gedecomprimeerd, wordt het resulterende bericht doorgegeven aan de stack voor de kanaalstack die moet worden verwerkt. Tijdens de compressie wordt het resulterende gecomprimeerde bericht rechtstreeks naar de opgegeven stream geschreven.

In dit voorbeeld worden helpermethoden (CompressBuffer en DecompressBuffer) gebruikt om conversie van buffers naar streams uit te voeren om de GZipStream klasse te gebruiken.

De gebufferde ReadMessage klassen WriteMessage maken gebruik van de BufferManager klasse. De encoder is alleen toegankelijk via de encoderfactory. De abstracte MessageEncoderFactory klasse biedt een eigenschap die is benoemd Encoder voor toegang tot de huidige encoder en een methode die is benoemd CreateSessionEncoder voor het maken van een encoder die sessies ondersteunt. Een dergelijke encoder kan worden gebruikt in het scenario waarin het kanaal sessies ondersteunt, is gerangschikt en betrouwbaar is. In dit scenario is optimalisatie mogelijk in elke sessie van de gegevens die naar de kabel worden geschreven. Als dit niet gewenst is, mag de basismethode niet overbelast worden. De Encoder eigenschap biedt een mechanisme voor toegang tot de sessieloze encoder en de standaard implementatie van de CreateSessionEncoder methode retourneert de waarde van de eigenschap. Omdat het voorbeeld een bestaande encoder verpakt om compressie te bieden, accepteert de MessageEncoderFactory implementatie een MessageEncoderFactory die de interne encoderfactory vertegenwoordigt.

Nu de encoder- en encoderfactory zijn gedefinieerd, kunnen ze worden gebruikt met een WCF-client en -service. Deze encoders moeten echter worden toegevoegd aan de kanaalstack. U kunt klassen afleiden van de ServiceHost en ChannelFactory<TChannel> klassen en de OnInitialize methoden overschrijven om deze encoderfactory handmatig toe te voegen. U kunt de encoderfactory ook beschikbaar maken via een aangepast bindingselement.

Als u een nieuw aangepast bindingselement wilt maken, moet u een klasse afleiden uit de BindingElement klasse. Er zijn echter verschillende typen bindingselementen. Om ervoor te zorgen dat het aangepaste bindingselement wordt herkend als een berichtcoderingselement, moet u ook het MessageEncodingBindingElement. De MessageEncodingBindingElement methode maakt een methode beschikbaar voor het maken van een nieuwe berichtencoderingsfactory (CreateMessageEncoderFactory), die wordt geïmplementeerd om een exemplaar van de overeenkomende berichtencoderingsfactory te retourneren. Daarnaast heeft de MessageEncodingBindingElement eigenschap een eigenschap om de adresseringsversie aan te geven. Omdat dit voorbeeld de bestaande encoders verpakt, verpakt de voorbeeld-implementatie ook de bestaande coderingsbindingselementen en neemt een binnenste coderingsbindingselement als parameter voor de constructor en maakt deze beschikbaar via een eigenschap. De volgende voorbeeldcode toont de implementatie van de GZipMessageEncodingBindingElement klasse.

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

Houd er rekening mee dat GZipMessageEncodingBindingElement de klasse de IPolicyExportExtension interface implementeert, zodat dit bindingselement kan worden geëxporteerd als beleid in metagegevens, zoals wordt weergegeven in het volgende voorbeeld.

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

De GZipMessageEncodingBindingElementImporter klasse implementeert de IPolicyImportExtension interface, dit klasse importeert beleid voor GZipMessageEncodingBindingElement. Svcutil.exe hulpprogramma kan worden gebruikt voor het importeren van beleidsregels in het configuratiebestand, moet GZipMessageEncodingBindingElementhet volgende worden toegevoegd aan 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>

Nu er een overeenkomend bindingselement voor de compressiecoderingsprogramma is, kan het programmatisch worden gekoppeld aan de service of client door een nieuw aangepast bindingsobject te maken en het aangepaste bindingselement eraan toe te voegen, zoals wordt weergegeven in de volgende voorbeeldcode.

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

Hoewel dit mogelijk voldoende is voor de meeste gebruikersscenario's, is het ondersteunen van een bestandsconfiguratie essentieel als een service wordt gehost op het web. Ter ondersteuning van het web-hostende scenario moet u een aangepaste configuratiehandler ontwikkelen zodat een aangepast bindingselement kan worden geconfigureerd in een bestand.

U kunt een configuratiehandler bouwen voor het bindingselement boven op het configuratiesysteem. De configuratiehandler voor het bindingselement moet zijn afgeleid van de BindingElementExtensionElement klasse. Hiermee BindingElementExtensionElement.BindingElementType wordt het configuratiesysteem geïnformeerd over het type bindingselement dat voor deze sectie moet worden gemaakt. Alle aspecten van de BindingElement set kunnen worden weergegeven als eigenschappen in de BindingElementExtensionElement afgeleide klasse. De ConfigurationPropertyAttribute hulp bij het toewijzen van de kenmerken van het configuratie-element aan de eigenschappen en het instellen van standaardwaarden als er kenmerken ontbreken. Nadat de waarden uit de configuratie zijn geladen en toegepast op de eigenschappen, wordt de BindingElementExtensionElement.CreateBindingElement methode aangeroepen, waarmee de eigenschappen worden geconverteerd naar een concreet exemplaar van een bindingselement. De BindingElementExtensionElement.ApplyConfiguration methode wordt gebruikt om de eigenschappen van de BindingElementExtensionElement afgeleide klasse te converteren naar de waarden die moeten worden ingesteld op het zojuist gemaakte bindingselement.

De volgende voorbeeldcode toont de implementatie van de 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;
    }
}

Deze configuratiehandler wordt toegewezen aan de volgende weergave in de App.config of Web.config voor de service of client.

<gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />

Als u deze configuratiehandler wilt gebruiken, moet deze worden geregistreerd in het <element system.serviceModel> , zoals wordt weergegeven in de volgende voorbeeldconfiguratie.

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

Wanneer u de server uitvoert, worden de bewerkingsaanvragen en -antwoorden weergegeven in het consolevenster. Druk op Enter in het venster om de server af te sluiten.

Press Enter key to Exit.

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

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

Wanneer u de client uitvoert, worden de bewerkingsaanvragen en -antwoorden weergegeven in het consolevenster. Druk op Enter in het clientvenster om de client af te sluiten.

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

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

Press <ENTER> to terminate client.

Het voorbeeld instellen, compileren en uitvoeren

  1. Installeer ASP.NET 4.0 met de volgende opdracht:

    %windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enable
    
  2. Zorg ervoor dat u de eenmalige installatieprocedure voor de Windows Communication Foundation-voorbeelden hebt uitgevoerd.

  3. Volg de instructies in Het bouwen van de Windows Communication Foundation-voorbeelden om de oplossing te bouwen.

  4. Als u het voorbeeld wilt uitvoeren in een configuratie met één of meerdere computers, volgt u de instructies in Het uitvoeren van de Windows Communication Foundation-voorbeelden.