在前面两篇文章WCF进阶:将消息正文Base64编码和WCF进阶:为每个操作附加身份信息中讲述了如何通过拦截消息的方式来记录或者修改消息,这种方式在特定条件下可以改变消息编码格式,但实现方式并不规范,而且使用范围也有限制。 WCF缺省提供了三种编码器(MessageEncoder):TextMessageEncoder,BinaryMessageEncoder,MtomMessageEncoder。事实上也是基于XML可以有三种格式:Text,Binary,MTOM,而XmlDictionaryWriter也提供了三种创建Writer的方法,CreateTextWriter,CreateBinaryWriter,CreateMtomWriter他们分别用于将XML以文本,二进制,MTOM保存。三种保存形式各有利弊,Text便于理解,更通用,Binary体积小,MTOM是优化之后的二进制,适用于较大的二进制传输。但无论使用哪种,最终在网络上传输的都是字节流或者叫字节数组。在Binding中处于最后一个的总是TransportBindingElement,也就是说当要传递的数据到达TransportBindingElement之后,其实已经是字节数组了。在TransportBindingElement之上,可以有事务处理器,会话处理器,消息安全处理器,编码器,传输安全处理器等,在编码器之前,主要的处理对象是Message,而之后,主要的处理对象就是Stream(Byte[]),从这点我们也就清楚了之前在学习安全体系的时候,总是习惯将安全划分为消息级别的安全和传输级别安全两种了。我们这次要实现的其实是将已经经过编码器编码好的字节流压缩后传输,而不是传统意义上的消息编码。这点也需要大家深层次的理解。

虽然我们实现的传输层面上的压缩编码,但实现机理和自定义MessageEncoder是一样的,MessageEncoder是所有编码器的基类,它有几个非常重要的方法和属性

public abstract Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType);
public ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager);
// Properties
public abstract string ContentType { get; }
public abstract string MediaType { get; }
public abstract MessageVersion MessageVersion { get; }

ReadMessage和WriteMessage是完成消息 <->字节数组转换的,这两个方法(还有几个重载),在实现自定义编码器的时候是最为重要的,我们主要是通过重写它们来完成自定义转换。上面我们也说过了,我们要实现的是传输层的压缩编码,那么需要有一个缺省编码,为此我们设计了名为CompressEncoder的自定义编码类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Channels;
using System.IO;namespace RobinLib
{public class CompressEncoder : MessageEncoder{CompressEncoderFactory factory;MessageEncoder innserEncoder;private CompressAlgorithm algorithm;public CompressEncoder(CompressEncoderFactory encoderFactory, CompressAlgorithm algorithm){factory = encoderFactory;this.algorithm = algorithm;innserEncoder = factory.InnerMessageEncodingBindingElement.CreateMessageEncoderFactory().Encoder;}public override string ContentType{get { return innserEncoder.ContentType; }}public override string MediaType{get { return innserEncoder.MediaType; }}public override MessageVersion MessageVersion{get { return innserEncoder.MessageVersion; }}public override bool IsContentTypeSupported(string contentType){return innserEncoder.IsContentTypeSupported(contentType);}public override T GetProperty<T>(){return innserEncoder.GetProperty<T>();}public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType){ArraySegment<byte> bytes = new Compressor(algorithm).DeCompress(buffer);int totalLength = bytes.Count;byte[] totalBytes = bufferManager.TakeBuffer(totalLength);Array.Copy(bytes.Array, 0, totalBytes, 0, bytes.Count);ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, 0, bytes.Count);bufferManager.ReturnBuffer(byteArray.Array); Message msg = innserEncoder.ReadMessage(byteArray, bufferManager, contentType);return msg;}public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders, string contentType){//读取消息的时候,二进制流为加密的,需要解压Stream ms = new Compressor(algorithm).DeCompress(stream); Message msg = innserEncoder.ReadMessage(ms, maxSizeOfHeaders, contentType);return msg;}public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset){ ArraySegment<byte> bytes = innserEncoder.WriteMessage(message, maxMessageSize, bufferManager);ArraySegment<byte> buffer = new Compressor(algorithm).Compress(bytes);int totalLength = buffer.Count + messageOffset;byte[] totalBytes = bufferManager.TakeBuffer(totalLength);Array.Copy(buffer.Array, 0, totalBytes, messageOffset, buffer.Count);ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, buffer.Count);Console.WriteLine("算法:"+algorithm+",原来字节流大小:"+bytes.Count+",压缩后字节流大小:"+byteArray.Count);return byteArray;}public override void WriteMessage(Message message, System.IO.Stream stream){System.IO.MemoryStream ms = new System.IO.MemoryStream();innserEncoder.WriteMessage(message, ms);stream = new Compressor(algorithm).Compress(ms);}}
}

在这个类中需要知道上层编码器是什么,我们用MessageEncoder innserEncoder来指定,在WriteMessage时候,将消息用内置编码器转换为字节数组,然后用压缩算法压缩这个数组,形成压缩后字节数组传递给到下一层,而在读取Message的时候,首先将收到的字节数组解压缩,最后将解压缩后字节数组用内置编码器转换为Message对象。其中Compressor是一个功能类,用于将字节数组压缩或者解压缩,代码为:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
namespace RobinLib
{public class Compressor{private CompressAlgorithm algorithm;public Compressor(CompressAlgorithm algorithm){this.algorithm = algorithm;}//压缩数组public ArraySegment<byte> Compress(ArraySegment<byte> data){MemoryStream ms = new MemoryStream();if (algorithm == CompressAlgorithm.GZip){Stream compressStream = new GZipStream(ms, CompressionMode.Compress, true);compressStream.Write(data.Array, 0, data.Count);compressStream.Close();}else{Stream compressStream = new DeflateStream(ms, CompressionMode.Compress, true);compressStream.Write(data.Array, 0, data.Count);compressStream.Close();}byte[] newByteArray = new byte[ms.Length];ms.Seek(0, SeekOrigin.Begin);ms.Read(newByteArray, 0, newByteArray.Length);ArraySegment<byte> bytes = new ArraySegment<byte>(newByteArray);return bytes;}//压缩流public Stream Compress(Stream stream){MemoryStream ms = new MemoryStream();if (algorithm == CompressAlgorithm.GZip){Stream compressStream = new GZipStream(ms, CompressionMode.Compress, true);byte[] buffer = new byte[stream.Length];stream.Read(buffer, 0, buffer.Length);compressStream.Write(buffer, 0, buffer.Length);compressStream.Close();}else{Stream compressStream = new DeflateStream(ms, CompressionMode.Compress, true);byte[] buffer = new byte[stream.Length];stream.Read(buffer, 0, buffer.Length);compressStream.Write(buffer, 0, buffer.Length);compressStream.Close();}return ms;}//解压缩数组public ArraySegment<byte> DeCompress(ArraySegment<byte> data){MemoryStream ms = new MemoryStream();ms.Write(data.Array, 0, data.Count);ms.Seek(0, SeekOrigin.Begin);if (algorithm == CompressAlgorithm.GZip){Stream compressStream = new GZipStream(ms, CompressionMode.Decompress, false);byte[] newByteArray = RetrieveBytesFromStream(compressStream, 1);compressStream.Close();return new ArraySegment<byte>(newByteArray);}else{Stream compressStream = new DeflateStream(ms, CompressionMode.Decompress, false);byte[] newByteArray = RetrieveBytesFromStream(compressStream, 1);compressStream.Close();return new ArraySegment<byte>(newByteArray);}}//解压缩数组public Stream DeCompress(Stream stream){stream.Seek(0, SeekOrigin.Begin);if (algorithm == CompressAlgorithm.GZip){Stream compressStream = new GZipStream(stream, CompressionMode.Decompress, false);byte[] newByteArray = RetrieveBytesFromStream(compressStream, 1);compressStream.Close();return new MemoryStream(newByteArray);}else{Stream compressStream = new DeflateStream(stream, CompressionMode.Decompress, false);byte[] newByteArray = RetrieveBytesFromStream(compressStream, 1);compressStream.Close();return new MemoryStream(newByteArray);}}public static byte[] RetrieveBytesFromStream(Stream stream, int bytesblock){List<byte> lst = new List<byte>();byte[] data = new byte[1024];int totalCount = 0;while (true){int bytesRead = stream.Read(data, 0, data.Length);if (bytesRead == 0){break;}byte[] buffers = new byte[bytesRead];Array.Copy(data, buffers, bytesRead);lst.AddRange(buffers);totalCount += bytesRead;}return lst.ToArray();}}
}

到此,其实我们的自定义编码器应该编写好了,接下来如何使用它成为我们最为关心的事情。每一个MessageEncoder都对应一个MessageEncoderFactory,在MessageEncodingBindingElement中能返回这个MessageEncoderFactory,然后通过在自定义BindingElement创建监听通道(BuildChannelListener)和通道工厂(BuildChannelFactory)的时候,将BindingElement添加到BindingContext,这样就能最终消费我们上面实现的CompressEncoder。

CompressEncoderFactory的代码实现为:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Channels;namespace RobinLib
{public class CompressEncoderFactory:MessageEncoderFactory{private MessageEncodingBindingElement innerMessageEncodingBindingElement;CompressEncoder messageEncoder;private CompressAlgorithm algorithm;public CompressEncoderFactory(MessageEncodingBindingElement innerMessageEncodingBindingElement, CompressAlgorithm algorithm){this.innerMessageEncodingBindingElement = innerMessageEncodingBindingElement;this.algorithm = algorithm;messageEncoder = new CompressEncoder(this,algorithm);}public override MessageEncoder CreateSessionEncoder(){return base.CreateSessionEncoder();}public override MessageEncoder Encoder{get { return messageEncoder; }}public override MessageVersion MessageVersion{get { return innerMessageEncodingBindingElement.MessageVersion; }}public MessageEncodingBindingElement InnerMessageEncodingBindingElement{get{return innerMessageEncodingBindingElement;}}}
}

自定义的MessageEncoderBindingElement代码为:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Channels;
using System.ServiceModel;
using System.Xml;namespace RobinLib
{public sealed class CompressEncodingBindingElement : MessageEncodingBindingElement{ private XmlDictionaryReaderQuotas readerQuotas;private MessageEncodingBindingElement innerMessageEncodingBindingElement;private CompressAlgorithm algorithm;public MessageEncodingBindingElement InnerMessageEncodingBindingElement{get{return innerMessageEncodingBindingElement;}}public CompressAlgorithm CompressAlgorithm{get{return algorithm;}}public CompressEncodingBindingElement(MessageEncodingBindingElement innerMessageEncodingBindingElement, CompressAlgorithm algorithm){this.readerQuotas = new XmlDictionaryReaderQuotas();this.algorithm = algorithm;this.innerMessageEncodingBindingElement = innerMessageEncodingBindingElement;}public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context){context.BindingParameters.Add(this);return context.BuildInnerChannelFactory<TChannel>();}public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context){context.BindingParameters.Add(this);return context.BuildInnerChannelListener<TChannel>();}public override bool CanBuildChannelFactory<TChannel>(BindingContext context){context.BindingParameters.Add(this);return context.CanBuildInnerChannelFactory<TChannel>();}public override bool CanBuildChannelListener<TChannel>(BindingContext context){context.BindingParameters.Add(this);return context.CanBuildInnerChannelListener<TChannel>();}public override MessageEncoderFactory CreateMessageEncoderFactory(){return new CompressEncoderFactory(innerMessageEncodingBindingElement,algorithm);}public override T GetProperty<T>(BindingContext context)  {if (typeof(T) == typeof(XmlDictionaryReaderQuotas)){return this.readerQuotas as T;}return base.GetProperty<T>(context);}public override MessageVersion MessageVersion{get{return innerMessageEncodingBindingElement.MessageVersion;}set{innerMessageEncodingBindingElement.MessageVersion = value;}}public override BindingElement Clone(){return new CompressEncodingBindingElement(innerMessageEncodingBindingElement,algorithm);} }
}

最终,我们可以使用CustomeBinding创建宿主和客户端。

服务端:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Robin_Wcf_CustomMessageEncoder_SvcLib;
using System.ServiceModel.Channels;
using RobinLib;namespace Robin_Wcf_CustomMessageEncoder_Host
{class Program{static void Main(string[] args){//服务地址Uri baseAddress = new Uri("http://127.0.0.1:8081/Robin_Wcf_Formatter");ServiceHost host = new ServiceHost(typeof(Service1), new Uri[] { baseAddress });//服务绑定ICollection<BindingElement> bindingElements = new List<BindingElement>();HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();CompressEncodingBindingElement textBindingElement = new CompressEncodingBindingElement(new TextMessageEncodingBindingElement(),CompressAlgorithm.Deflate);bindingElements.Add(textBindingElement);bindingElements.Add(httpBindingElement);CustomBinding bind = new CustomBinding(bindingElements);  host.AddServiceEndpoint(typeof(IService1), bind, "");if (host.Description.Behaviors.Find<System.ServiceModel.Description.ServiceMetadataBehavior>() == null){System.ServiceModel.Description.ServiceMetadataBehavior svcMetaBehavior = new System.ServiceModel.Description.ServiceMetadataBehavior();svcMetaBehavior.HttpGetEnabled = true;svcMetaBehavior.HttpGetUrl = new Uri("http://127.0.0.1:8001/Mex");host.Description.Behaviors.Add(svcMetaBehavior);}host.Opened += new EventHandler(delegate(object obj, EventArgs e){Console.WriteLine("服务已经启动!");}); host.Open();Console.Read();}}
}

客户端:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RobinLib;
using System.ServiceModel.Channels;
using Robin_Wcf_CustomMessageEncoder_ClientApp.ServiceReference1;namespace Robin_Wcf_CustomMessageEncoder_ClientApp
{class Program{static void Main(string[] args){System.Threading.Thread.Sleep(5300);ICollection<BindingElement> bindingElements = new List<BindingElement>();HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();CompressEncodingBindingElement textBindingElement = new CompressEncodingBindingElement(new BinaryMessageEncodingBindingElement(), CompressAlgorithm.GZip);bindingElements.Add(textBindingElement);bindingElements.Add(httpBindingElement); CustomBinding bind = new CustomBinding(bindingElements);  ServiceReference1.IService1 svc = new ServiceReference1.Service1Client(bind, new System.ServiceModel.EndpointAddress("http://127.0.0.1:8081/Robin_Wcf_Formatter"));string pres = svc.GetData(10);Console.WriteLine(pres);CompositeType ct = svc.GetDataUsingDataContract(new CompositeType());System.IO.MemoryStream ms = new System.IO.MemoryStream();for (int i = 0; i < 1000000; i++){byte[] buffer = BitConverter.GetBytes(i);ms.Write(buffer, 0, buffer.Length);}System.IO.Stream stream = svc.GetStream(ms);Console.Read();}}
}

我们可以更改CompressEncodingBindingElement textBindingElement = new CompressEncodingBindingElement(new BinaryMessageEncodingBindingElement(), CompressAlgorithm.GZip);,指定内置MessageEncoder和压缩算法。

最后附件一句,不是所有的数组压缩后体积都变小的,只有文本类型的压缩后,效果比较明显。运行程序,当内置TextMessageEncodingBindingElement的时候,我们得到的效果为:

此时说明压缩效果非常明显,

而当内置BinaryMessageEncodingBindingElement的时候,压缩效果不再突出,甚至起到反作用。

如果有朋友需要使用压缩传输,可以直接下载项目,引用其中的RobinLib.dll,然后使用自定义Binding。

项目文件:/jillzhang/Robin_Wcf_CustomMessageEncoder.rar

下文我们将演示实现对称加密传输。

转载于:https://www.cnblogs.com/jillzhang/archive/2010/04/13/1711079.html

WCF进阶:将编码后的字节流压缩传输相关推荐

  1. WCF 进阶: 对称加密传输

    大家使用WCF的时候,会不会觉得使用SSL通道传输太麻烦,使用明文传输又觉得不安全呢? 特别是当传递的消息中带有比较敏感,机密的身份信息的时候更是如此呢?我们在上文实现了压缩编码传输,详见WCF进阶: ...

  2. 基于哈夫曼编码对文件进行压缩和解压缩(详细讲解)

    基于哈夫曼编码对文件进行压缩和解压缩(详细讲解) 本文对应c++代码实现链接 一.背景 利用特定的算法来压缩数据的工具,压缩后生成的文件称为压缩包.如果想使用其中的数据,就得用压缩软件对数据进行解压. ...

  3. WCF进阶:为每个操作附加身份信息

    上文WCF进阶:将消息正文Base64编码中介绍了实现自定义MessageInspector来记录消息和实现自定义Formatter来改写消息,本文介绍一下在WCF中使用SoapHeader进行验证的 ...

  4. internetreadfile读取数据长度为0_Go发起HTTP2.0请求流程分析(后篇)——标头压缩

    阅读建议 这是HTTP2.0系列的最后一篇,笔者推荐阅读顺序如下: Go中的HTTP请求之--HTTP1.1请求流程分析 Go发起HTTP2.0请求流程分析(前篇) Go发起HTTP2.0请求流程分析 ...

  5. 利用huffman编码对文本文件进行压缩与解压(java实现)

    利用huffman编码对文本文件进行压缩与解压 输入:一个文本文件 输出:压缩后的文件 算法过程: (1)统计文本文件中每个字符的使用频度 (2)构造huffman编码 (3)以二进制流形式压缩文件 ...

  6. 使用哈夫曼编码实现数据的压缩和解压(java版)

    1.哈夫曼树 哈夫曼编码使用哈夫曼树的数据结构,哈夫曼树图解如下,即构造一个带权路径最小的数: 2.哈夫曼编码 使用哈夫曼树生成哈夫曼编码,已实现减少传输中数据的冗余:截取网络课程中的几张图来说明: ...

  7. 学弟学妹们,学会霍夫曼编码后,再也不用担心网络带宽了!

    CSDN 的学弟学妹们,大家好,我是沉默王二. 今天来给大家普及一下霍夫曼编码(Huffman Coding),一种用于无损数据压缩的熵编码算法,由美国计算机科学家大卫·霍夫曼在 1952 年提出-- ...

  8. 哈夫曼编码及文本文件的压缩解压(c++SourceCode)

    哈夫曼编码是一种编码方式,是可变字长编码(VLC)的一种.以哈夫曼树-即最优二叉树,带权路径长度最小的二叉树,经常应用于数据 压缩. 在计算机信息处理中,"哈夫曼编码"是一种一致性 ...

  9. 基于Huffman编码的文档压缩

    算法思想: 统计文件中字符种类个数和各种字符个数,根据词频统计构建赫夫曼树并根据赫夫曼树找出各字符对应的编码,再根据各字符的编码对文件进行压缩,最后重构赫夫曼树,根据赫夫曼树和二进制编码对压缩文件进行 ...

最新文章

  1. jieba词性说明字典
  2. PS 图像尺寸|点阵格式图像|矢量格式图像|图像格式的选择
  3. 如何给a标签绑定ajax事件
  4. php 会话控制 文件上传
  5. KKT条件和拉格朗日乘子法
  6. 如何更改CPropertySheet的背景色
  7. 最新 UI 色彩渐变素材模板|设计师好帮手
  8. iOS - OC RunLoop 运行循环/消息循环
  9. 设计模式のFactoryPattern(工厂模式)----创建模式
  10. SVN的配置与使用方法
  11. html——相对路径、绝对路径(有待补充....)
  12. NOIP2013Day1
  13. 深入了解JVM的底层原理
  14. Whois 查询违法?
  15. 【C#】ADO.NET 实体数据模型 warning CS1591:
  16. 书籍_《未来世界的幸存者》阮一峰--2/5
  17. Pr:抠像与视频合成
  18. 计算机网络_选择题(一)
  19. 【C语言进阶】二、指针
  20. C#下ECDsa签名、验签

热门文章

  1. 三星的S3C2440A 存储器控制器
  2. C语言中sizeof详解——面试C/C++
  3. 攻防比赛_2020年度泉州市大学生网络安全攻防比赛在黎明职业大学圆满落幕
  4. pytorch中切换虚拟环境
  5. 使用CNN进行情感分类
  6. LeetCode 网易-1. 分割环(前缀和 + 哈希)
  7. LeetCode 1599. 经营摩天轮的最大利润(模拟)
  8. LeetCode 186. 翻转字符串里的单词 II
  9. LeetCode 1354. 多次求和构造目标数组(优先队列+逆向思考)
  10. LeetCode 114. 二叉树展开为链表(递归)