Bagikan melalui


Encoder Pesan Kustom: Encoder Kompresi

Sampel Kompresi menunjukkan cara mengimplementasikan encoder kustom menggunakan platform Windows Communication Foundation (WCF).

Detail Sampel

Sampel ini terdiri dari program konsol klien (.exe), program konsol layanan yang dihost sendiri (.exe) dan pustaka encoder pesan kompresi (.dll). Layanan ini menerapkan kontrak yang mendefinisikan pola komunikasi balasan permintaan. Kontrak didefinisikan oleh ISampleServer antarmuka, yang mengekspos operasi gema string dasar (Echo dan BigEcho). Klien membuat permintaan sinkron ke operasi tertentu dan layanan membalas dengan mengulangi pesan kembali ke klien. Aktivitas klien dan layanan terlihat di jendela konsol. Tujuan dari sampel ini adalah untuk menunjukkan cara menulis encoder kustom dan menunjukkan dampak kompresi pesan pada kawat. Anda dapat menambahkan instrumentasi ke encoder pesan kompresi untuk menghitung ukuran pesan, waktu pemrosesan, atau keduanya.

Nota

Dalam .NET Framework 4, dekompresi otomatis telah diaktifkan pada klien WCF jika server mengirim respons terkompresi (dibuat dengan algoritma seperti GZip atau Deflate). Jika layanan dihosting Web di Server Informasi Internet (IIS), maka IIS dapat dikonfigurasi agar layanan dapat mengirim respons terkompresi. Sampel ini dapat digunakan jika persyaratannya adalah melakukan kompresi dan dekompresi pada klien dan layanan atau jika layanan dihost sendiri.

Sampel menunjukkan cara membangun dan mengintegrasikan encoder pesan kustom ke dalam aplikasi WCF. Pustaka GZipEncoder.dll diterapkan dengan baik pada klien maupun layanan. Sampel ini juga menunjukkan dampak pemadatan pesan. Kode dalam GZipEncoder.dll menunjukkan hal berikut:

  • Membangun encoder kustom dan pabrik encoder.

  • Mengembangkan elemen pengikatan untuk encoder kustom.

  • Menggunakan konfigurasi pengikatan kustom untuk mengintegrasikan elemen pengikatan kustom.

  • Mengembangkan handler konfigurasi kustom untuk memungkinkan konfigurasi elemen pengikatan kustom melalui file.

Seperti yang ditunjukkan sebelumnya, ada beberapa lapisan yang diimplementasikan dalam encoder kustom. Untuk mengilustrasikan hubungan antara masing-masing lapisan ini dengan lebih baik, urutan peristiwa yang disederhanakan untuk start-up layanan ada dalam daftar berikut:

  1. Server dimulai.

  2. Informasi konfigurasi telah dibaca.

    1. Konfigurasi layanan mendaftarkan handler konfigurasi kustom.

    2. Host layanan sudah dibuat dan dibuka.

    3. Elemen konfigurasi kustom membuat dan mengembalikan elemen pengikatan kustom.

    4. Elemen pengikatan kustom membuat dan mengembalikan sebuah pabrik pembuat enkoder pesan.

  3. Pesan diterima.

  4. Pabrik encoder pesan mengembalikan encoder pesan untuk membaca pesan dan menulis keluar tanggapan.

  5. Lapisan encoder diimplementasikan sebagai class factory. Hanya kelas pabrik pengode yang harus diekspos secara publik untuk pengode kustom. Objek pabrik dikembalikan oleh elemen pengikatan saat objek ServiceHost atau ChannelFactory<TChannel> dibuat. Encoder pesan dapat beroperasi dalam mode buffer atau streaming. Sampel ini menunjukkan mode buffer dan mode streaming.

Untuk setiap mode ada metode yang menyertai ReadMessage dan WriteMessage pada kelas abstrak MessageEncoder . Sebagian besar pekerjaan pengodean terjadi dalam metode ini. Sampel membungkus encoder teks dan pesan biner yang ada. Ini memungkinkan sampel untuk mendelegasikan pembacaan dan penulisan representasi kawat pesan ke encoder dalam dan memungkinkan encoder kompresi untuk mengompresi atau mendekompresi hasilnya. Karena tidak ada alur untuk pengodean pesan, ini adalah satu-satunya model untuk menggunakan beberapa encoder di WCF. Setelah pesan didekompresi, pesan yang dihasilkan diteruskan ke atas tumpukan untuk ditangani oleh tumpukan saluran. Selama pemadatan, pesan terkompresi yang dihasilkan ditulis langsung ke aliran yang disediakan.

Sampel ini menggunakan metode pembantu (CompressBuffer dan DecompressBuffer) untuk melakukan konversi dari buffer ke aliran untuk menggunakan GZipStream kelas .

Kelas ReadMessage dan WriteMessage yang di-buffer memanfaatkan kelas BufferManager. Encoder hanya dapat diakses melalui pabrik encoder. Kelas abstrak MessageEncoderFactory menyediakan properti bernama Encoder untuk mengakses encoder saat ini dan metode bernama CreateSessionEncoder untuk membuat encoder yang mendukung sesi. Encoder semacam itu dapat digunakan dalam skenario di mana saluran mendukung sesi, diurutkan dan dapat diandalkan. Skenario ini memungkinkan optimalisasi di setiap sesi data yang ditransmisikan melalui jaringan. Jika ini tidak diinginkan, metode dasar tidak boleh kelebihan beban. Properti Encoder menyediakan mekanisme untuk mengakses encoder tanpa sesi dan implementasi CreateSessionEncoder default metode mengembalikan nilai properti. Karena sampel membungkus encoder yang ada untuk memberikan kompresi, MessageEncoderFactory implementasi menerima MessageEncoderFactory yang mewakili pabrik encoder dalam.

Sekarang, setelah encoder dan pabrik encoder didefinisikan, mereka dapat digunakan dengan klien dan layanan WCF. Namun, encoder ini harus ditambahkan ke tumpukan saluran. Anda dapat menurunkan kelas dari kelas ServiceHost dan ChannelFactory<TChannel> dan menimpa metode OnInitialize untuk menambahkan pabrik encoder ini secara manual. Anda juga dapat mengekspos pabrik encoder melalui elemen pengikatan kustom.

Untuk membuat elemen pengikatan kustom baru, dapatkan kelas dari BindingElement kelas . Namun, ada beberapa jenis elemen pengikatan. Untuk memastikan bahwa elemen pengikatan kustom dikenali sebagai elemen pengikatan pengodean pesan, Anda juga harus menerapkan MessageEncodingBindingElement. Fungsi MessageEncodingBindingElement menyediakan metode untuk membuat pabrik encoder pesan baru (CreateMessageEncoderFactory), yang diimplementasikan untuk mengembalikan instance dari pabrik encoder pesan yang sesuai. Selain itu, MessageEncodingBindingElement memiliki properti untuk menunjukkan versi alamat. Karena sampel ini membungkus encoder yang ada, implementasi sampel juga membungkus elemen pengikatan encoder yang sudah ada dan mengambil elemen pengikatan encoder internal sebagai parameter untuk konstruktor, kemudian mengeksposnya melalui sebuah properti. Contoh kode berikut menunjukkan implementasi GZipMessageEncodingBindingElement kelas.

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

Perhatikan bahwa GZipMessageEncodingBindingElement kelas mengimplementasikan IPolicyExportExtension antarmuka, sehingga elemen pengikatan ini dapat diekspor sebagai kebijakan dalam metadata, seperti yang ditunjukkan dalam contoh berikut.

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

Kelas GZipMessageEncodingBindingElementImporter mengimplementasikan IPolicyImportExtension antarmuka, kelas ini mengimpor kebijakan untuk GZipMessageEncodingBindingElement. alat Svcutil.exe dapat digunakan untuk mengimpor kebijakan konfigurasi ke file konfigurasi, untuk menangani GZipMessageEncodingBindingElement, maka yang berikut harus ditambahkan ke 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>

Sekarang setelah ada elemen pengikatan yang cocok untuk encoder kompresi, itu dapat secara terprogram terhubung ke layanan atau klien dengan membuat objek pengikatan kustom baru dan menambahkan elemen pengikatan kustom ke dalamnya, seperti yang ditunjukkan dalam kode sampel berikut.

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

Meskipun ini mungkin cukup untuk sebagian besar skenario pengguna, mendukung konfigurasi file sangat penting jika layanan akan dihosting Web. Untuk mendukung skenario yang dihosting Web, Anda harus mengembangkan handler konfigurasi kustom untuk memungkinkan elemen pengikatan kustom dapat dikonfigurasi dalam file.

Anda dapat membangun handler konfigurasi untuk elemen pengikatan di atas sistem konfigurasi. Handler konfigurasi untuk elemen pengikatan harus berasal dari BindingElementExtensionElement kelas . Elemen BindingElementExtensionElement.BindingElementType menginformasikan sistem konfigurasi tentang tipe elemen pengikatan yang harus dibuat untuk bagian ini. Semua aspek BindingElement yang dapat diatur harus diekspos sebagai properti di BindingElementExtensionElement kelas turunan. ConfigurationPropertyAttribute membantu dalam mengaitkan atribut dari elemen konfigurasi ke properti dan menetapkan nilai standar jika atribut hilang. Setelah nilai dari konfigurasi dimuat dan diterapkan ke properti, BindingElementExtensionElement.CreateBindingElement metode ini dipanggil, yang mengonversi properti menjadi instans konkret dari elemen pengikatan. Metode BindingElementExtensionElement.ApplyConfiguration ini digunakan untuk mengonversi properti pada BindingElementExtensionElement kelas turunan menjadi nilai yang akan diatur pada elemen pengikatan yang baru dibuat.

Kode sampel berikut menunjukkan implementasi 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;
    }
}

Handler konfigurasi ini memetakan ke representasi berikut di App.config atau Web.config untuk layanan atau klien.

<gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />

Untuk menggunakan handler konfigurasi ini, handler harus didaftarkan dalam <elemen system.serviceModel> , seperti yang ditunjukkan dalam konfigurasi sampel berikut.

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

Saat Anda menjalankan server, permintaan operasi dan respons ditampilkan di jendela konsol. Tekan ENTER di jendela untuk mematikan server.

Press Enter key to Exit.

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

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

Saat Anda menjalankan klien, permintaan dan respons operasi ditampilkan di jendela konsol. Tekan ENTER di jendela klien untuk mematikan klien.

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

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

Press <ENTER> to terminate client.

Untuk menyiapkan, mengompilasi, dan menjalankan sampel

  1. Instal ASP.NET 4.0 menggunakan perintah berikut:

    %windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enable
    
  2. Pastikan Anda telah melakukan Prosedur Penyiapan One-Time untuk Sampel Windows Communication Foundation.

  3. Untuk membangun solusi, ikuti instruksi dalam Membangun Sampel Windows Communication Foundation.

  4. Untuk menjalankan sampel dalam konfigurasi satu atau lintas komputer, ikuti instruksi di Menjalankan Sampel Windows Communication Foundation.