Megosztás:


Egyéni üzenetkódoló: Tömörítési kódoló

A tömörítési minta bemutatja, hogyan implementálhat egyéni kódolót a Windows Communication Foundation (WCF) platform használatával.

Minta részletei

Ez a minta egy ügyfélkonzolprogramból (.exe), egy saját üzemeltetésű szolgáltatáskonzolprogramból (.exe) és egy tömörítési üzenetkódoló kódtárból (.dll) áll. A szolgáltatás megvalósít egy szerződést, amely egy kérés-válasz kommunikációs mintát határoz meg. A szerződést az ISampleServer interfész határozza meg, amely biztosítja az alapvető szöveg visszhangzás műveleteket (Echo és BigEcho). Az ügyfél szinkronizált kéréseket küld egy adott művelethez, és a szolgáltatás válaszol az üzenetnek az ügyfélnek való ismételt megismétlésével. Az ügyfél- és szolgáltatástevékenység a konzolablakokban látható. A minta célja, hogy bemutassa, hogyan írhat egyéni kódolót, és hogyan szemléltetheti az üzenetek tömörítésének hatását a vezetéken. A tömörítési üzenetkódolóhoz instrumentálást adhat az üzenet méretének, a feldolgozási időnek vagy mindkettőnek a kiszámításához.

Megjegyzés:

A .NET-keretrendszer 4-ben az automatikus dekompresszió engedélyezve van a WCF kliensen, ha a kiszolgáló tömörített választ küld, amelyet valamilyen algoritmus, például GZip vagy Deflate hozott létre. Ha a szolgáltatás az Internet Information Server (IIS) webszolgáltatása, akkor az IIS konfigurálható úgy, hogy a szolgáltatás tömörített választ küldjön. Ez a minta akkor használható, ha a követelmény a tömörítés és a dekompresszió mind az ügyfélen, mind a szolgáltatáson, vagy ha a szolgáltatás saját üzemeltetésű.

A minta bemutatja, hogyan hozhat létre és integrálhat egyéni üzenetkódolót egy WCF-alkalmazásba. A kódtár GZipEncoder.dll az ügyféllel és a szolgáltatással együtt is üzembe van helyezve. Ez a minta az üzenetek tömörítésének hatását is bemutatja. A GZipEncoder.dll kódja a következőket mutatja be:

  • Egyéni kódoló és kódológyár létrehozása.

  • Kötéselem fejlesztése egyéni kódolóhoz.

  • Egyéni kötési konfiguráció használata egyéni kötéselemek integrálásához.

  • Egyéni konfigurációkezelő fejlesztése egyéni kötéselemek fájlkonfigurációjának engedélyezéséhez.

Ahogy korábban jeleztük, több réteg is létezik, amelyek egy egyéni kódolóban vannak implementálva. Az egyes rétegek közötti kapcsolat jobb szemléltetéséhez a szolgáltatásindítási események egyszerűsített sorrendje az alábbi listában található:

  1. A kiszolgáló elindul.

  2. A konfigurációs információkat beolvasták.

    1. A szolgáltatáskonfiguráció regisztrálja az egyéni konfigurációkezelőt.

    2. A szolgáltatás hosztja létrejön és megnyílik.

    3. Az egyéni konfigurációs elem létrehozza és visszaadja az egyéni kötési elemet.

    4. Az egyéni kötéselem létrehoz és visszaad egy üzenetkódoló-előállítót.

  3. Üzenet érkezik.

  4. Az üzenetkódoló-előállító egy üzenetkódolót ad vissza az üzenetben való olvasáshoz és a válasz írásához.

  5. A kódoló réteg osztály-előállítóként van implementálva. Az egyéni kódolóhoz csak a kódolóosztály-előállítót szabad nyilvánosan elérhetővé tenni. A gyári objektumot a kötési elem adja vissza, amikor a ServiceHost vagy a ChannelFactory<TChannel> objektum létrejön. Az üzenetkódolók pufferelt vagy streamelési módban is működhetnek. Ez a minta a pufferelt és a streamelési módot is bemutatja.

Minden módhoz tartozik egy kísérő metódus: ReadMessage és WriteMessage az absztrakt MessageEncoder osztályban. A kódolási munka nagy része ezekben a metódusokban történik. A minta bevonja a meglévő szöveg- és bináris üzenetkódolókat. Ez lehetővé teszi, hogy a minta delegálja az üzenetek vezetékes ábrázolásának olvasását és írását a belső kódolónak, valamint azt is, hogy a tömörítési kódoló tömörítse vagy kitömörítse az eredményeket. Mivel nincs folyamat az üzenetkódoláshoz, ez az egyetlen modell, amely több kódolót használ a WCF-ben. Az üzenet kibontása után az eredményként kapott üzenet átkerül a csatorna verem kezelésére szolgáló verembe. A tömörítés során az eredményül kapott tömörített üzenet közvetlenül a megadott streambe lesz írva.

Ez a minta segédmetórákat (CompressBuffer és DecompressBuffer) használ a pufferekből streamekké való átalakítás végrehajtásához az GZipStream osztály használatához.

A pufferelt ReadMessage és WriteMessage az osztályok használják az osztályt BufferManager . A kódoló csak a kódoló-előállítón keresztül érhető el. Az absztrakt MessageEncoderFactory osztály egy tulajdonságot biztosít, amely az aktuális kódoló elérésére szolgál Encoder , valamint egy munkameneteket támogató kódoló létrehozására szolgáló CreateSessionEncoder metódust. Ez a kódoló abban a forgatókönyvben használható, amikor a csatorna támogatja a munkameneteket, rendezett és megbízható. Ez a forgatókönyv lehetővé teszi az optimalizálást a vezetékre írt adatok minden egyes munkamenetében. Ha ez nem kívánatos, az alapmetódus nem terhelhető túl. A Encoder tulajdonság egy mechanizmust biztosít a munkamenet nélküli kódoló eléréséhez, és a CreateSessionEncoder metódus alapértelmezett implementációja a tulajdonság értékét adja vissza. Mivel a minta egy meglévő kódolót csomagol a tömörítéshez, az MessageEncoderFactory implementáció elfogadja a MessageEncoderFactory-et, ami a belső kódoló gyárat képviseli.

Most, hogy a kódoló és a kódológyár definiálva van, WCF-ügyféllel és szolgáltatással is használhatók. Azonban ezeket a kódolókat hozzá kell adni a csatornaköteghez. A ServiceHost és ChannelFactory<TChannel> osztályokból származtathat osztályokat, és felülbírálhatja a OnInitialize metódusokat a kódoló-gyár manuális hozzáadásához. A kódoló gyárat egyedi kötési elemen keresztül is elérhetővé teheti.

Új egyéni kötéselem létrehozásához hozzon létre egy osztályt az BindingElement osztályból. A kötési elemeknek azonban számos típusa van. Annak biztosításához, hogy az egyéni kötéselem üzenetkódoló kötéselemként legyen felismerve, azt is meg kell valósítania, hogy a MessageEncodingBindingElement. Ez MessageEncodingBindingElement egy új üzenetkódoló-előállító (CreateMessageEncoderFactory) létrehozására szolgáló metódust tesz elérhetővé, amely a megfelelő üzenetkódoló-előállító egy példányát adja vissza. Emellett a MessageEncodingBindingElement címzési verziót jelző tulajdonság is rendelkezik. Mivel ez a minta integrálja a meglévő kódolókat, a mintafelépítés a meglévő kódoló kötési elemeit is integrálja, és egy belső kódoló kötési elemet vesz fel paraméterként a konstruktorban, amelyet egy tulajdonságon keresztül tesz hozzáférhetővé. Az alábbi mintakód az osztály implementációját GZipMessageEncodingBindingElement mutatja be.

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

Vegye figyelembe, hogy GZipMessageEncodingBindingElement az osztály implementálja az IPolicyExportExtension interfészt, így ez a kötési elem exportálható szabályzatként a metaadatokban, ahogyan az az alábbi példában is látható.

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

Az GZipMessageEncodingBindingElementImporter osztály implementálja a IPolicyImportExtension interfészt, és importálási szabályzatot alkalmaz a GZipMessageEncodingBindingElement-hez. Svcutil.exe eszközzel házirendeket importálhat a konfigurációs fájlba. A(z) GZipMessageEncodingBindingElement kezeléséhez a következőket kell hozzáadnia Svcutil.exe.config-hez.

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

Most, hogy van egy megfelelő kötési elem a tömörítési kódoló számára, programozott módon beköthető a szolgáltatásba vagy az ügyfélbe egy új, egyéni kötési objektum létrehozásával és az egyéni kötési elem hozzáadásával, ahogy az az alábbi mintakódban látható.

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

Bár ez elegendő lehet a felhasználói forgatókönyvek többségéhez, a fájlkonfiguráció támogatása kritikus fontosságú, ha egy szolgáltatást webre kell üzemeltetni. A webes forgatókönyv támogatásához egyéni konfigurációkezelőt kell kidolgoznia, hogy lehetővé tegye egy egyéni kötéselem konfigurálását egy fájlban.

A kötési elemhez konfigurációkezelőt hozhat létre a konfigurációs rendszer tetején. A kötéselem konfigurációkezelőjének az osztályból BindingElementExtensionElement kell származnia. A BindingElementExtensionElement.BindingElementType konfigurációs rendszer tájékoztatja a szakaszhoz létrehozandó kötési elem típusáról. A beállítható elemek minden aspektusát BindingElement tulajdonságokként kell elérhetővé tenni a BindingElementExtensionElement származtatott osztályban. A ConfigurationPropertyAttribute konfigurációelem-attribútumok tulajdonságokhoz való leképezésében és az alapértelmezett értékek beállításában segít, ha az attribútumok hiányoznak. A konfiguráció értékeinek betöltése és a tulajdonságokra való alkalmazása után a rendszer meghívja a BindingElementExtensionElement.CreateBindingElement metódust, amely a tulajdonságokat egy kötéselem konkrét példányává alakítja. A BindingElementExtensionElement.ApplyConfiguration metódust arra használják, hogy a BindingElementExtensionElement származtatott osztály tulajdonságait olyan értékekké konvertálják, amelyeket az újonnan létrehozott kötéselemen kell beállítani.

Az alábbi mintakód bemutatja a GZipMessageEncodingElement megvalósítását.

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

Ez a konfigurációkezelő a szolgáltatás vagy ügyfél App.config vagy Web.config a következő ábrázolásra képez le.

<gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />

A konfigurációkezelő használatához regisztrálni kell a <system.serviceModel> elemen belül, ahogyan az az alábbi mintakonfigurációban látható.

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

A kiszolgáló futtatásakor a műveleti kérelmek és a válaszok megjelennek a konzolablakban. Nyomja le az ENTER billentyűt az ablakban a kiszolgáló leállításához.

Press Enter key to Exit.

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

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

Az ügyfél futtatásakor a műveleti kérelmek és a válaszok megjelennek a konzolablakban. Nyomja le az ENTER billentyűt az ügyfélablakban az ügyfél leállításához.

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

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

Press <ENTER> to terminate client.

A példa beállítása, elkészítése és futtatása

  1. Telepítse a ASP.NET 4.0-t a következő paranccsal:

    %windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enable
    
  2. Győződjön meg arról, hogy elvégezte a Windows Communication Foundation-minták One-Time beállítási eljárását.

  3. A megoldás létrehozásához kövesse a Windows Communication Foundation-minták készítésére vonatkozó utasításokat.

  4. Ha a mintát egy vagy több gép közötti konfigurációban szeretné futtatni, kövesse A Windows Communication Foundation-minták futtatásacímű témakör utasításait.