博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WCF中自定义消息编码器:压缩编码器的使用
阅读量:5161 次
发布时间:2019-06-13

本文共 16013 字,大约阅读时间需要 53 分钟。

原文:

通过抓包知道WCF在提交、返回数据的时候大多使用XML进行数据交互,如果返回DataTable那么这些数据将变得很大,通过查询找到一个对数据压缩的方法:

新增项目GZipEncoder,GzipEncoder中增加三个文件 :

GZipMessageEncoderFactory.cs

using System;using System.Collections.Generic;using System.Configuration;using System.Text;using System.IO;using System.IO.Compression;using System.Runtime.Serialization;using System.Runtime.InteropServices;using System.Security;using System.ServiceModel;using System.ServiceModel.Channels;using System.ServiceModel.Configuration;using System.Xml;using System.ServiceModel.Security;using System.ServiceModel.Description;namespace GZipEncoder{    //This class is used to create the custom encoder (GZipMessageEncoder)    internal class GZipMessageEncoderFactory : MessageEncoderFactory    {        MessageEncoder encoder;        //The GZip encoder wraps an inner encoder        //We require a factory to be passed in that will create this inner encoder        public GZipMessageEncoderFactory(MessageEncoderFactory messageEncoderFactory)        {            if (messageEncoderFactory == null)                throw new ArgumentNullException("messageEncoderFactory", "A valid message encoder factory must be passed to the GZipEncoder");            encoder = new GZipMessageEncoder(messageEncoderFactory.Encoder);        }        //The service framework uses this property to obtain an encoder from this encoder factory        public override MessageEncoder Encoder        {            get { return encoder; }        }        public override MessageVersion MessageVersion        {            get { return encoder.MessageVersion; }        }        //This is the actual GZip encoder        class GZipMessageEncoder : MessageEncoder        {            static string GZipContentType = "application/x-gzip";            //This implementation wraps an inner encoder that actually converts a WCF Message            //into textual XML, binary XML or some other format. This implementation then compresses the results.            //The opposite happens when reading messages.            //This member stores this inner encoder.            MessageEncoder innerEncoder;            //We require an inner encoder to be supplied (see comment above)            internal GZipMessageEncoder(MessageEncoder messageEncoder)                : base()            {                if (messageEncoder == null)                    throw new ArgumentNullException("messageEncoder", "A valid message encoder must be passed to the GZipEncoder");                innerEncoder = messageEncoder;            }            //public override string CharSet            //{            //    get { return ""; }            //}            public override string ContentType            {                get { return GZipContentType; }            }            public override string MediaType            {                get { return GZipContentType; }            }            //SOAP version to use - we delegate to the inner encoder for this            public override MessageVersion MessageVersion            {                get { return innerEncoder.MessageVersion; }            }            //Helper method to compress an array of bytes            static ArraySegment
CompressBuffer(ArraySegment
buffer, BufferManager bufferManager, int messageOffset) { MemoryStream memoryStream = new MemoryStream(); memoryStream.Write(buffer.Array, 0, messageOffset); using (GZipStream gzStream = new GZipStream(memoryStream, CompressionMode.Compress, true)) { gzStream.Write(buffer.Array, messageOffset, buffer.Count); } byte[] compressedBytes = memoryStream.ToArray(); byte[] bufferedBytes = bufferManager.TakeBuffer(compressedBytes.Length); Array.Copy(compressedBytes, 0, bufferedBytes, 0, compressedBytes.Length); bufferManager.ReturnBuffer(buffer.Array); ArraySegment
byteArray = new ArraySegment
(bufferedBytes, messageOffset, bufferedBytes.Length - messageOffset); return byteArray; } //Helper method to decompress an array of bytes static ArraySegment
DecompressBuffer(ArraySegment
buffer, BufferManager bufferManager) { MemoryStream memoryStream = new MemoryStream(buffer.Array, buffer.Offset, buffer.Count - buffer.Offset); MemoryStream decompressedStream = new MemoryStream(); int totalRead = 0; int blockSize = 1024; byte[] tempBuffer = bufferManager.TakeBuffer(blockSize); using (GZipStream gzStream = new GZipStream(memoryStream, CompressionMode.Decompress)) { while (true) { int bytesRead = gzStream.Read(tempBuffer, 0, blockSize); if (bytesRead == 0) break; decompressedStream.Write(tempBuffer, 0, bytesRead); totalRead += bytesRead; } } bufferManager.ReturnBuffer(tempBuffer); byte[] decompressedBytes = decompressedStream.ToArray(); byte[] bufferManagerBuffer = bufferManager.TakeBuffer(decompressedBytes.Length + buffer.Offset); Array.Copy(buffer.Array, 0, bufferManagerBuffer, 0, buffer.Offset); Array.Copy(decompressedBytes, 0, bufferManagerBuffer, buffer.Offset, decompressedBytes.Length); ArraySegment
byteArray = new ArraySegment
(bufferManagerBuffer, buffer.Offset, decompressedBytes.Length); bufferManager.ReturnBuffer(buffer.Array); return byteArray; } //One of the two main entry points into the encoder. Called by WCF to decode a buffered byte array into a Message. public override Message ReadMessage(ArraySegment
buffer, BufferManager bufferManager, string contentType) { //Decompress the buffer ArraySegment
decompressedBuffer = DecompressBuffer(buffer, bufferManager); //Use the inner encoder to decode the decompressed buffer Message returnMessage = innerEncoder.ReadMessage(decompressedBuffer, bufferManager); returnMessage.Properties.Encoder = this; return returnMessage; } //One of the two main entry points into the encoder. Called by WCF to encode a Message into a buffered byte array. public override ArraySegment
WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) { //Use the inner encoder to encode a Message into a buffered byte array ArraySegment
buffer = innerEncoder.WriteMessage(message, maxMessageSize, bufferManager, messageOffset); //Compress the resulting byte array return CompressBuffer(buffer, bufferManager, messageOffset); } public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders, string contentType) { GZipStream gzStream = new GZipStream(stream, CompressionMode.Decompress, true); return innerEncoder.ReadMessage(gzStream, maxSizeOfHeaders); } public override void WriteMessage(Message message, System.IO.Stream stream) { using (GZipStream gzStream = new GZipStream(stream, CompressionMode.Compress, true)) { innerEncoder.WriteMessage(message, gzStream); } // innerEncoder.WriteMessage(message, gzStream) depends on that it can flush data by flushing // the stream passed in, but the implementation of GZipStream.Flush will not flush underlying // stream, so we need to flush here. stream.Flush(); } } }}

  GZipMessageEncodingBindingElement.cs

using System;using System.Xml;using System.ServiceModel;using System.Configuration;using System.ServiceModel.Channels;using System.ServiceModel.Configuration;using System.ServiceModel.Description;namespace GZipEncoder{    // This is constants for GZip message encoding policy.    static class GZipMessageEncodingPolicyConstants    {        public const string GZipEncodingName = "GZipEncoding";        public const string GZipEncodingNamespace = "http://schemas.microsoft.com/ws/06/2004/mspolicy/netgzip1";        public const string GZipEncodingPrefix = "gzip";    }    //This is the binding element that, when plugged into a custom binding, will enable the GZip encoder    public sealed class GZipMessageEncodingBindingElement                        : MessageEncodingBindingElement //BindingElement                        , IPolicyExportExtension    {        //We will 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 will create 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
(BindingContext context) { if (typeof(T) == typeof(XmlDictionaryReaderQuotas)) { return innerBindingElement.GetProperty
(context); } else { return base.GetProperty
(context); } } public override IChannelFactory
BuildChannelFactory
(BindingContext context) { if (context == null) throw new ArgumentNullException("context"); context.BindingParameters.Add(this); return context.BuildInnerChannelFactory
(); } public override IChannelListener
BuildChannelListener
(BindingContext context) { if (context == null) throw new ArgumentNullException("context"); context.BindingParameters.Add(this); return context.BuildInnerChannelListener
(); } public override bool CanBuildChannelListener
(BindingContext context) { if (context == null) throw new ArgumentNullException("context"); context.BindingParameters.Add(this); return context.CanBuildInnerChannelListener
(); } 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)); } } //This class is necessary to be able to plug in the GZip encoder binding element through //a configuration file 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; } }}

  GZipMessageEncodingBindingElementImporter.cs

using System;using System.Xml;using System.ServiceModel.Description;using System.Xml.Schema;using System.Collections.ObjectModel;using System.Collections.Generic;using System.Text;namespace GZipEncoder{    public class GZipMessageEncodingBindingElementImporter : IPolicyImportExtension    {        public GZipMessageEncodingBindingElementImporter()        {        }        void IPolicyImportExtension.ImportPolicy(MetadataImporter importer, PolicyConversionContext context)        {            if (importer == null)            {                throw new ArgumentNullException("importer");            }            if (context == null)            {                throw new ArgumentNullException("context");            }            ICollection
assertions = context.GetBindingAssertions(); foreach (XmlElement assertion in assertions) { if ((assertion.NamespaceURI == GZipMessageEncodingPolicyConstants.GZipEncodingNamespace) && (assertion.LocalName == GZipMessageEncodingPolicyConstants.GZipEncodingName) ) { assertions.Remove(assertion); context.BindingElements.Add(new GZipMessageEncodingBindingElement()); break; } } } }}

  WCF Server 和 Client 都引用项目 GZipEncoder,然后修改config文件

Server web.config 中增加:

 

  客户端的app.config 配置:

  这样就完成了WCF的数据压缩,其他代码都不用动,直接可以使用

      通过抓包看到Send和Recv的数据都为 application/x-gzip 格式

      

 

      

 

 另外出现第一次调用WCF很慢的解决方法是将 useDefaultWebProxy 这个属性改成false,不让它找代理。这样就会在程序第一次调用WCF的时候快很多。

 

 

 

posted on
2015-07-10 09:59 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/lonelyxmas/p/4634936.html

你可能感兴趣的文章
如何将应用完美迁移至Android P版本
查看>>
【转】清空mysql一个库中的所有表的数据
查看>>
基于wxPython的python代码统计工具
查看>>
淘宝JAVA中间件Diamond详解(一)---简介&快速使用
查看>>
Hadoop HBase概念学习系列之HBase里的宽表设计概念(表设计)(二十七)
查看>>
Kettle学习系列之Kettle能做什么?(三)
查看>>
Day03:Selenium,BeautifulSoup4
查看>>
awk变量
查看>>
mysql_对于DQL 的简单举例
查看>>
35. Search Insert Position(C++)
查看>>
[毕业生的商业软件开发之路]C#异常处理
查看>>
一些php文件函数
查看>>
有关快速幂取模
查看>>
Linux运维必备工具
查看>>
字符串的查找删除
查看>>
NOI2018垫底记
查看>>
快速切题 poj 1002 487-3279 按规则处理 模拟 难度:0
查看>>
Codeforces Round #277 (Div. 2)
查看>>
【更新】智能手机批量添加联系人
查看>>
NYOJ-128前缀式计算
查看>>