WCF消息压缩
对于WCF应用来说,传输前压缩请求消息和回复消息,不但可以降低网络流量,也可以提高网络传输的性能
一、消息压缩方案
二、用于数据压缩与解压缩组件
三、用于消息压缩与解压的组件
四、用于对请求/回复消息压缩和解压缩的组件
五、将CompressionMessageFormatter用于WCF运行时框架的操作行为
六、查看结构压缩后的消息
七、扩展
一、消息压缩方案
消息压缩在WCF中的实现其实很简单,我们只需要在消息(请求消息/回复消息)被序列化之后,发送之前进行压缩;在接收之后,反序列化之前进行解压缩即可。针对压缩/解压缩使用的时机,有三种典型的解决方案。通过自定义MessageEncoder和MessageEncodingBindingElement 来完成。
1.将编码后的字节流压缩传输
2.创建用于压缩和解压缩的信道
3. 自定义MessageFormatter实现序列化后的压缩和法序列化前的解压缩
这里要介绍的解决方案3。
二、用于数据压缩与解压缩组件
我们支持两种方式的压缩,Dflate和GZip。两种不同的压缩算法通过如下定义的CompressionAlgorithm枚举表示。
public enum CompressionAlgorithm
{
GZip,
Deflate
}
而如下定义的DataCompressor负责基于上述两种压缩算法实际上的压缩和解压缩工作。
internal class DataCompressor
{
public static byte[] Compress(byte[] decompressedData, CompressionAlgorithm algorithm)
{
using (MemoryStream stream = new MemoryStream())
{
if (algorithm == CompressionAlgorithm.Deflate)
{
GZipStream stream2 = new GZipStream(stream, CompressionMode.Compress, true);
stream2.Write(decompressedData, , decompressedData.Length);
stream2.Close();
}
else
{
DeflateStream stream3 = new DeflateStream(stream, CompressionMode.Compress, true);
stream3.Write(decompressedData, , decompressedData.Length);
stream3.Close();
}
return stream.ToArray();
}
} public static byte[] Decompress(byte[] compressedData, CompressionAlgorithm algorithm)
{
using (MemoryStream stream = new MemoryStream(compressedData))
{
if (algorithm == CompressionAlgorithm.Deflate)
{
using (GZipStream stream2 = new GZipStream(stream, CompressionMode.Decompress))
{
return LoadToBuffer(stream2);
}
}
else
{
using (DeflateStream stream3 = new DeflateStream(stream, CompressionMode.Decompress))
{
return LoadToBuffer(stream3);
}
}
}
} private static byte[] LoadToBuffer(Stream stream)
{
using (MemoryStream stream2 = new MemoryStream())
{
int num;
byte[] buffer = new byte[0x400];
while ((num = stream.Read(buffer, , buffer.Length)) > )
{
stream2.Write(buffer, , num);
}
return stream2.ToArray();
}
}
}
三、用于消息压缩与解压的组件
而针对消息的压缩和解压缩通过如下一个MessageCompressor来完成。具体来说,我们通过上面定义的DataCompressor对消息的主体部分内容进行压缩,并将压缩后的内容存放到一个预定义的XML元素中(名称和命名空间分别为CompressedBody和http://www.yswenli.net/comporession/),同时添加相应的MessageHeader表示消息经过了压缩,以及采用的压缩算法。对于解压缩,则是通过消息是否具有相应的MessageHeader判断该消息是否经过压缩,如果是则根据相应的算法对其进行解压缩。
具体的实现如下:
public class MessageCompressor
{
public MessageCompressor(CompressionAlgorithm algorithm)
{
this.Algorithm = algorithm;
}
public Message CompressMessage(Message sourceMessage)
{
byte[] buffer;
using (XmlDictionaryReader reader1 = sourceMessage.GetReaderAtBodyContents())
{
buffer = Encoding.UTF8.GetBytes(reader1.ReadOuterXml());
}
if (buffer.Length == )
{
Message emptyMessage = Message.CreateMessage(sourceMessage.Version, (string)null);
sourceMessage.Headers.CopyHeadersFrom(sourceMessage);
sourceMessage.Properties.CopyProperties(sourceMessage.Properties);
emptyMessage.Close();
return emptyMessage;
}
byte[] compressedData = DataCompressor.Compress(buffer, this.Algorithm);
string copressedBody = CompressionUtil.CreateCompressedBody(compressedData);
XmlTextReader reader = new XmlTextReader(new StringReader(copressedBody), new NameTable());
Message message2 = Message.CreateMessage(sourceMessage.Version, null, (XmlReader)reader);
message2.Headers.CopyHeadersFrom(sourceMessage);
message2.Properties.CopyProperties(sourceMessage.Properties);
message2.AddCompressionHeader(this.Algorithm);
sourceMessage.Close();
return message2;
} public Message DecompressMessage(Message sourceMessage)
{
if (!sourceMessage.IsCompressed())
{
return sourceMessage;
}
CompressionAlgorithm algorithm = sourceMessage.GetCompressionAlgorithm();
sourceMessage.RemoveCompressionHeader();
byte[] compressedBody = sourceMessage.GetCompressedBody();
byte[] decompressedBody = DataCompressor.Decompress(compressedBody, algorithm);
string newMessageXml = Encoding.UTF8.GetString(decompressedBody);
XmlTextReader reader2 = new XmlTextReader(new StringReader(newMessageXml));
Message newMessage = Message.CreateMessage(sourceMessage.Version, null, reader2);
newMessage.Headers.CopyHeadersFrom(sourceMessage);
newMessage.Properties.CopyProperties(sourceMessage.Properties);
return newMessage;
}
public CompressionAlgorithm Algorithm { get; private set; }
}
下面是针对Message类型而定义了一些扩展方法和辅助方法。
public static class CompressionUtil
{
public const string CompressionMessageHeader = "Compression";
public const string CompressionMessageBody = "CompressedBody";
public const string Namespace = "http://www.yswenli.net/compression"; public static bool IsCompressed(this Message message)
{
return message.Headers.FindHeader(CompressionMessageHeader, Namespace) > -;
} public static void AddCompressionHeader(this Message message, CompressionAlgorithm algorithm)
{
message.Headers.Add(MessageHeader.CreateHeader(CompressionMessageHeader, Namespace, string.Format("algorithm = \"{0}\"", algorithm)));
} public static void RemoveCompressionHeader(this Message message)
{
message.Headers.RemoveAll(CompressionMessageHeader, Namespace);
} public static CompressionAlgorithm GetCompressionAlgorithm(this Message message)
{
if (message.IsCompressed())
{
var algorithm = message.Headers.GetHeader<string>(CompressionMessageHeader, Namespace);
algorithm = algorithm.Replace("algorithm =", string.Empty).Replace("\"", string.Empty).Trim();
if (algorithm == CompressionAlgorithm.Deflate.ToString())
{
return CompressionAlgorithm.Deflate;
} if (algorithm == CompressionAlgorithm.GZip.ToString())
{
return CompressionAlgorithm.GZip;
}
throw new InvalidOperationException("Invalid compression algrorithm!");
}
throw new InvalidOperationException("Message is not compressed!");
} public static byte[] GetCompressedBody(this Message message)
{
byte[] buffer;
using (XmlReader reader1 = message.GetReaderAtBodyContents())
{
buffer = Convert.FromBase64String(reader1.ReadElementString(CompressionMessageBody, Namespace));
}
return buffer;
} public static string CreateCompressedBody(byte[] content)
{
StringWriter output = new StringWriter();
using (XmlWriter writer2 = XmlWriter.Create(output))
{
writer2.WriteStartElement(CompressionMessageBody, Namespace);
writer2.WriteBase64(content, , content.Length);
writer2.WriteEndElement();
}
return output.ToString();
}
}
四、用于对请求/回复消息压缩和解压缩的组件
消息的序列化和反序列化最终是通过MessageFormatter来完成的。具体来说,客户端通过ClientMessageFormatter实现对请求消息的序列化和对回复消息的序列化,而服务端通过DispatchMessageFormatter实现对请求消息的反序列化和对回复消息的序列化。
在默认的情况下,WCF选用的MessageFormatter为DataContractSerializerOperationFormatter,它采用DataContractSerializer进行实际的序列化和法序列化操作。我们自定义的MessageFormatter实际上是对DataContractSerializerOperationFormatter的封装,我们依然使用它来完成序列化和反序列化工作,额外实现序列化后的压缩和法序列化前的解压缩。
因为DataContractSerializerOperationFormatter是一个internal类型,我们只有通过反射的方式来创建它。如下的代码片断为用于进行消息压缩与解压缩的自定义MessageFormatter,即CompressionMessageFormatter的定义。
public class CompressionMessageFormatter : IDispatchMessageFormatter, IClientMessageFormatter
{
private const string DataContractSerializerOperationFormatterTypeName = "System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; public IDispatchMessageFormatter InnerDispatchMessageFormatter { get; private set; }
public IClientMessageFormatter InnerClientMessageFormatter { get; private set; }
public MessageCompressor MessageCompressor { get; private set; } public CompressionMessageFormatter(CompressionAlgorithm algorithm, OperationDescription description, DataContractFormatAttribute dataContractFormatAttribute, DataContractSerializerOperationBehavior serializerFactory)
{
this.MessageCompressor = new MessageCompressor(algorithm);
Type innerFormatterType = Type.GetType(DataContractSerializerOperationFormatterTypeName);
var innerFormatter = Activator.CreateInstance(innerFormatterType, description, dataContractFormatAttribute, serializerFactory);
this.InnerClientMessageFormatter = innerFormatter as IClientMessageFormatter;
this.InnerDispatchMessageFormatter = innerFormatter as IDispatchMessageFormatter;
} public void DeserializeRequest(Message message, object[] parameters)
{
message = this.MessageCompressor.DecompressMessage(message);
this.InnerDispatchMessageFormatter.DeserializeRequest(message, parameters);
} public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
var message = this.InnerDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);
return this.MessageCompressor.CompressMessage(message);
} public object DeserializeReply(Message message, object[] parameters)
{
message = this.MessageCompressor.DecompressMessage(message);
return this.InnerClientMessageFormatter.DeserializeReply(message, parameters);
} public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
var message = this.InnerClientMessageFormatter.SerializeRequest(messageVersion, parameters);
return this.MessageCompressor.CompressMessage(message);
}
}
五、将CompressionMessageFormatter用于WCF运行时框架的操作行为
ClientMessageFormatter和DispatchMessageFormatter实际上属于ClientOperation和DispatchOperation的组件。我们可以通过如下一个自定义的操作行为CompressionOperationBehaviorAttribute将其应用到相应的操作上。
[AttributeUsage(AttributeTargets.Method)]
public class CompressionOperationBehaviorAttribute : Attribute, IOperationBehavior
{
public CompressionAlgorithm Algorithm { get; set; } public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
clientOperation.SerializeRequest = true;
clientOperation.DeserializeReply = true;
var dataContractFormatAttribute = operationDescription.SyncMethod.GetCustomAttributes(typeof(DataContractFormatAttribute), true).FirstOrDefault() as DataContractFormatAttribute;
if (null == dataContractFormatAttribute)
{
dataContractFormatAttribute = new DataContractFormatAttribute();
} var dataContractSerializerOperationBehavior = operationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
clientOperation.Formatter = new CompressionMessageFormatter(this.Algorithm, operationDescription, dataContractFormatAttribute, dataContractSerializerOperationBehavior);
} public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.SerializeReply = true;
dispatchOperation.DeserializeRequest = true;
var dataContractFormatAttribute = operationDescription.SyncMethod.GetCustomAttributes(typeof(DataContractFormatAttribute), true).FirstOrDefault() as DataContractFormatAttribute;
if (null == dataContractFormatAttribute)
{
dataContractFormatAttribute = new DataContractFormatAttribute();
}
var dataContractSerializerOperationBehavior = operationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
dispatchOperation.Formatter = new CompressionMessageFormatter(this.Algorithm, operationDescription, dataContractFormatAttribute, dataContractSerializerOperationBehavior);
} public void Validate(OperationDescription operationDescription) { }
}
六、查看结构压缩后的消息
为了验证应用了CompressionOperationBehaviorAttribute特性的操作方法对应的消息是否经过了压缩,我们可以通过一个简单的例子来检验。我们采用常用的计算服务的例子,下面是服务契约和服务类型的定义。我们上面定义的CompressionOperationBehaviorAttribute应用到服务契约的Add操作上。
[ServiceContract(Namespace = "http://www.yswenli.net/")]
public interface ICalculator
{
[OperationContract]
[CompressionOperationBehavior]
double Add(double x, double y);
}
public class CalculatorService : ICalculator
{
public double Add(double x, double y)
{
return x + y;
}
}
我们采用BasicHttpBinding作为终结点的绑定类型(具体的配置请查看源代码),下面是通过Fiddler获取的消息的内容,它们的主体部分都经过了基于压缩的编码。
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Compression xmlns="http://www.yswenli.net/compression">algorithm = "GZip"</Compression>
</s:Header>
<s:Body>
<CompressedBody xmlns="http://www.yswenli.net/compression">7L0HYBx ... CQAA//8=</CompressedBody>
</s:Body>
</s:Envelope>
回复消息
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Compression xmlns="http://www.yswenli.net/compression">algorithm = "GZip"</Compression>
</s:Header>
<s:Body>
<CompressedBody xmlns="http://www.yswenli.net/compression">7L0H...PAAAA//8=</CompressedBody>
</s:Body>
</s:Envelope>
七、扩展
如果不想使微软自带的序列化或者因为某些原因(emoji字符异常等)可以使用自定义的IDispatchMessageInspector。由于CompressionMessageFormatter使用基于DataContractSerializer序列化器的DataContractSerializerOperationFormatter进行消息的序列化和发序列化工作,而DataContractSerializer仅仅是WCF用于序列化的一种默认的选择(WCF还可以采用传统的XmlSeriaizer);为了让CompressionMessageFormatter能够使用其他序列化器,可以对于进行相应的修正。
转载请标明本文来源:http://www.cnblogs.com/yswenli/p/6670081.html
更多内容欢迎我的的github:https://github.com/yswenli
如果发现本文有什么问题和任何建议,也随时欢迎交流~
WCF消息压缩的更多相关文章
- WCF 消息压缩性能问题及解决方法
最近使用WCF作为通迅框架开发一套信息系统,系统使用传统C/S框架,系统有可能会部署在互联网上,因此决定对传输的数据进行GZIP压缩,原来在使用.NET Remoting时,可以使用插入自定义的Cha ...
- 基于WCF的RESTFul WebAPI如何对传输内容实现压缩
前言 WCF作为通迅框架可以很容易地实现对消息的压缩,且方法不止一种,主要解决方法主要有以下四种: 1.通过自定义MessageEncoder和MessageEncodingBindingElemen ...
- [No0000131]WCF压缩传输方案整理
1.WCF进阶:将编码后的字节流压缩传输 2.通过WCF扩展实现消息压缩 3.WCF 消息压缩性能问题及解决方法
- WCF学习之旅—第三个示例之四(三十)
上接WCF学习之旅—第三个示例之一(二十七) WCF学习之旅—第三个示例之二(二十八) WCF学习之旅—第三个示例之三(二十九) ...
- 【WCF】使用“用户名/密码”验证的合理方法
我不敢说俺的方法是最佳方案,反正这世界上很多东西都是变动的,正像老子所说的——“反(返)者,道之动”.以往看到有些文章中说,为每个客户端安装证书嫌麻烦,就直接采用把用户名和密码塞在SOAP头中发送,然 ...
- 【WCF】错误协定声明
在上一篇烂文中,老周给大伙伴们介绍了 IErrorHandler 接口的使用,今天,老周补充一个错误处理的知识点——错误协定. 错误协定与IErrorHandler接口不同,大伙伴们应该记得,上回我们 ...
- 【WCF】自定义错误处理(IErrorHandler接口的用法)
当被调用的服务操作发生异常时,可以直接把异常的原始内容传回给客户端.在WCF中,服务器传回客户端的异常,通常会使用 FaultException,该异常由这么几个东东组成: 1.Action:在服务调 ...
- [WCF]缺少一行代码引发的血案
这是今天作项目支持的发现的一个关于WCF的问题,虽然最终我只是添加了一行代码就解决了这个问题,但是整个纠错过程是痛苦的,甚至最终发现这个问题都具有偶然性.具体来说,这是一个关于如何自动为服务接口(契约 ...
- 【原创经验分享】WCF之消息队列
最近都在鼓捣这个WCF,因为看到说WCF比WebService功能要强大许多,另外也看了一些公司的招聘信息,貌似一些中.高级的程序员招聘,都有提及到WCF这一块,所以,自己也关心关心一下,虽然目前工作 ...
随机推荐
- Angular.js!(附:聊聊非原生框架项目)
最近,为了项目接触了一个很火的前端框架Angular.js,下面就Angular做一个简介吧(大牛请绕步,只针对没有接触过angular的人). Angular.js是一款精简的前端框架,如果要追溯它 ...
- p1221网络布线(最小生成树 Prim(普里母)算法) p1222 Watering Hole
描述 Description 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场.当然,他需要你的帮助.约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路 ...
- 前端学PHP之日期与时间
前面的话 在Web程序开发时,时间发挥着重要的作用,不仅在数据存储和显示时需要日期和时间的参与,好多功能模块的开发,时间通常都是至关重要的.网页静态化需要判断缓存时间.页面访问消耗的时间需要计算.根据 ...
- angular : ng-animate : css 原理,详解
通过几中指令就能完成1.2.xx的animate ·ng-repeat ·ng-show,ng-hide ·ng-if,ng-include,ng-view ·ng-switch ·ng-class ...
- 手把手教你webpack、react和node.js环境配置(下篇)
上篇我介绍了前端下webpack和react.redux等环境的配置,这篇将继续重点介绍后台node.js的配置. 这里是上篇链接:手把手教你webpack.react和node.js环境配置(上篇) ...
- MySQL中的完整性约束条件(主键、外键、唯一、非空)
数据库的完整性约束用来防止对数据的意外破坏,来保证数据的安全性和一致性. 主键 1.创建表时候指定主键 创建表user(id, username, age),并且id字段非空自增. CREATE TA ...
- Ionic2开发笔记(2)创建子页面及其应用
1. 当你第一次产生ionic2应用程序,这是生成的项目结构 ├── ├── config.xml 这包含配置应用程序的名称,和包名,将被用于我们的应用程序安装到一个实际的设备. ├── h ...
- 使用php ajax写省、市、区、三级联动
题目要求: 要求:写一个省市区(或者年月日)的三级联动,实现地区或时间的下拉选择. 实现技术:php ajax 实现:省级下拉变化时市下拉区下拉跟着变化,市级下拉变化时区下拉跟着变化. 使用china ...
- H5 表单元素
HTML5 表单元素 HTML5 的新的表单元素: HTML5 拥有若干涉及表单的元素和属性. 本章介绍以下新的表单元素: datalist keygen output 浏览器支持 Input typ ...
- java基础之基础语法详录(一)
[前言] java的语法先从基础语法学,Java语言是由类和对象组成的,其对象和类又是由方法和变量组成,而方法,又包含了语句和表达式. 对象:(几乎)一切都是对象,比如:一只熊猫,他的外观,颜色,他在 ...