WCF中自定义消息编码器:压缩编码器的使用
通过抓包知道WCF在提交、返回数据的时候大多使用XML进行数据交互,如果返回DataTable那么这些数据将变得很大,通过查询找到一个对数据压缩的方法:
http://msdn.microsoft.com/zh-cn/library/ms751458(v=vs.110).aspx
新增项目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<byte> CompressBuffer(ArraySegment<byte> 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<byte> byteArray = new ArraySegment<byte>(bufferedBytes, messageOffset, bufferedBytes.Length - messageOffset); return byteArray;
} //Helper method to decompress an array of bytes
static ArraySegment<byte> DecompressBuffer(ArraySegment<byte> 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<byte> byteArray = new ArraySegment<byte>(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<byte> buffer, BufferManager bufferManager, string contentType)
{
//Decompress the buffer
ArraySegment<byte> 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<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
//Use the inner encoder to encode a Message into a buffered byte array
ArraySegment<byte> 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<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));
}
} //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<XmlElement> 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 中增加:
<?xml version="1.0" encoding="utf-8"?>
<configuration> <appSettings>
<!-- 省略 -->
</appSettings> <system.web>
<compilation debug="true" targetFramework="4.0" />
<customErrors mode="Off"/>
</system.web> <system.serviceModel> <!-- GZipEncoder 加密部分 Begin -->
<services><!-- name:命名空间.类名 -->
<service name="WcfService.action">
<endpoint address=""
binding="customBinding" bindingConfiguration="BufferedHttpSampleServer"
bindingName="BufferedHttpSampleServer"
contract="WcfService.Iaction" />
</service>
</services><!-- 注册 gzipMessageEncoding -->
<extensions>
<bindingElementExtensions>
<add name="gzipMessageEncoding"
type="GZipEncoder.GZipMessageEncodingElement, GZipEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
<bindings>
<customBinding>
<binding name="BufferedHttpSampleServer"><!-- 处理程序映射 -->
<gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />
<httpTransport hostNameComparisonMode="StrongWildcard"
manualAddressing="False"
maxReceivedMessageSize="65536"
authenticationScheme="Anonymous"
bypassProxyOnLocal="False"
realm=""
useDefaultWebProxy="True" />
</binding>
</customBinding>
</bindings>
<!-- GZipEncoder 加密部分 End --> <behaviors>
<serviceBehaviors>
<behavior>
<!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false 并删除上面的元数据终结点 -->
<serviceMetadata httpGetEnabled="true"/>
<!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> </system.serviceModel> <system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer> </configuration>
客户端的app.config 配置:
<?xml version="1.0"?>
<configuration> <startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
<supportedRuntime version="v2.0.50727"/>
</startup> <system.serviceModel>
<bindings>
<customBinding>
<binding name="WSHttpBinding_SwfBuilderService">
<gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />
<httpTransport manualAddressing="false" authenticationScheme="Anonymous"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
proxyAuthenticationScheme="Anonymous" realm="" useDefaultWebProxy="true" />
</binding>
</customBinding>
</bindings>
<client> <endpoint address="http://192.168.1.212/action.svc" binding="customBinding"
bindingConfiguration="WSHttpBinding_SwfBuilderService"
contract="WcfService.Iaction"
name="WSHttpBinding_SwfBuilderService">
</endpoint>
<metadata>
<policyImporters>
<extension type="GZipEncoder.GZipMessageEncodingBindingElementImporter, GZipEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</policyImporters>
</metadata> </client> <!-- gzip 扩展 -->
<extensions>
<bindingElementExtensions>
<add name="gzipMessageEncoding" type="GZipEncoder.GZipMessageEncodingElement, GZipEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bindingElementExtensions>
</extensions> </system.serviceModel> </configuration>
这样就完成了WCF的数据压缩,其他代码都不用动,直接可以使用
通过抓包看到Send和Recv的数据都为 application/x-gzip 格式


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

WCF中自定义消息编码器:压缩编码器的使用的更多相关文章
- Spring Security 5中的默认密码编码器
1.概述 在Spring Security 4中,可以使用内存中身份验证以纯文本格式存储密码. 对版本5中的密码管理过程进行了重大改进,为密码编码和解码引入了更安全的默认机制.这意味着如果您的Spri ...
- ios怎样实现快速将显卡中数据读出压缩成视频在cocos2dx扩展开发中
如果解决ios怎样实现快速将显卡中数据读出压缩成视频在cocos2dx扩展开发中 手机平台性能是个关键问题. 压缩视频分成3个步骤: 读取显卡数据, 使用编码器压缩,保存文件. 使用libav 压缩的 ...
- [No0000126]SSL/TLS原理详解与WCF中的WS-Security
SSL/TLS作为一种互联网安全加密技术 1. SSL/TLS概览 1.1 整体结构 SSL是一个介于HTTP协议与TCP之间的一个可选层,其位置大致如下: SSL:(Secure Socket La ...
- WCF中,通过C#代码或App.config配置文件创建ServiceHost类
C# static void Main(string[] args) { //创建宿主的基地址 Uri baseAddress = new Uri("http://localhost:808 ...
- WCF学习之旅—WCF中传统的异常处理(十六)
WCF中的异常处理 在软件开发过程中,不可能没有异常的出现,所以在开发过程中,对不可预知的异常进行解决时,异常处理显得尤为重要.对于一般的.NET系统来说,我们简单地借助try/catch可以很容易地 ...
- 在Wcf中应用ProtoBuf替代默认的序列化器
Google的ProtoBuf序列化器性能的牛逼已经有目共睹了,可以把它应用到Socket通讯,队列,Wcf中,身为dotnet程序员一边期待着不久后Grpc对dotnet core的支持更期待着Wc ...
- ajax调用本地wcf中的post和get
我们可以通过jQuery调用本地或者远程的wcf服务,本文讲解的是对本地wcf服务的post和get调用方式. post和get到底有什么区别呢?此处不作详述. 但是,post对请求的数据格式更为严格 ...
- 在 WCF 中使用高效的 BinaryFormatter 序列化
本文将定义一个 WCF 终结点行为扩展,以在 WCF 中使用更高效的 BinaryFormatter 进行二进制序列化,并实现对是否使用传统二进制序列化功能的可配置. 介绍 实现步骤 使用方法 效果 ...
- 【WCF】WCF中的InstanceContext与ConcurrencyMode【转】
一.前言 最近忙于公司的在线升级项目,一个人要负责公司四大产品的在线升级,这四个产品是在Revit中以插件形式存在的,目前基于WCF来实现.等客户总量突破5万了,再重新用socket实现. 由于有服务 ...
随机推荐
- 实例讲解虚拟机3种网络模式(桥接、nat、Host-only)
转自:http://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646007.html 前言 很多人安装虚拟机的时候,经常遇到不能上网的问题,而vmwa ...
- JNDI 是什么
转自:http://blog.csdn.net/zhaosg198312/article/details/3979435 JNDI是 Java 命名与目录接口(Java Naming and Dire ...
- C语言有字符串这种数据类型吗?
C/C++语言 用 char 数组 存放 字符串.例如: char str[]="abcd 1234";char *ss = "1234 XYZ";printf ...
- Android ActionBar 一步一步分析 (转)
原文摘自:http://blog.csdn.net/android2me/article/details/8874846 1.Action Bar 介绍 我们能在应用中看见的actionbar一般就是 ...
- CDH中,执行HIVE脚本表联查权限问题。。
文章来自http://www.cnblogs.com/hark0623/p/4174641.html 转发请注明 有时候执行表联查的时候总会出现没有权限写文件的情况. 这个时候使用sudo -H hi ...
- 初识API函数
我之前是一个只会编写数值计算的程序的OIer,但我并不甘于这种现状,于是我编写了我的第一个使用API函数的C++程序,开发平台是VS2012: // ConsoleApplication.cpp : ...
- Complete the Sequence[HDU1121]
Complete the Sequence Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Ot ...
- javascript reverse string
var strReversed = str.split('').reverse().join(''); function: function reverse(str){ return str.spli ...
- Linux下java获取CPU、内存、磁盘IO、网络带宽使用率
一.CPU 使用proc文件系统,"proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为访问系统内核数据的操作提供接口.用户和应用程序可以通过proc得 ...
- SHell string操作 转
本文也即<Learning the bash Shell>3rd Edition的第四章Basic Shell Programming之读书笔记之二,但我们将不限于此. String操作 ...