基于WCF的通道网络传输数据压缩技术的应用研究

本文及程序不是介绍WCF怎么用,而是研究如何在WCF通信时的通道两端自动进行数据压缩和解压缩,从而增加分布式数据传输速度。

而且,这个过程是完全透明的,用户及编程人员根本不需要知道它的存在,相当于HOOK在两端的一个组件。可以使用中网络带宽较小的网络环境中。当WCF在两个实体间通讯的时候,便自动创建一个信息通道转接通讯,这个消息包含数据请求和相应。WCF使用特殊的编码器将请求和响应数据转换成一系列的字节。

    我所带的项目里遇到大文件分布式传输问题,经过分析考虑采用WCF通道压缩技术来解决此问题。执行这样的编码是需要传输大文件(XML格式)由一台机器到另一台机器传输,而连接有速度限制。我不用写一个特殊的函数边压缩和边解压,而是配置传输通道可以做到这一点,这种方式压缩可重复使用的任何契约。我发现自己编写的消息编码器是最简单的方式来实现功能,真正的问题是如何编写信息编码器,在MSDN上没有找到任何关于此应用的实例。消息契约编码器的想法是Hook连接两端发送和接收信息的渠道。程序是采用Microsoft Visual Studio 2008 WCF设计。

附件: image.jpg

             
图1 WCF消息通道编码过程时序图   

发送方:代码中加入方法,该方法及其参数的序列化成SOAP消息,消息编码序列化的信息将成为一个字节数组,字节数组发送传输层。

    接收方:传输层接收字节数组,消息编码器并行化字节数组到一条消息,该方法及其参数并行化到一个SOAP消息,方法是被监听的。
    当加入压缩信息编码器,该方法要求有一点改变:
    发送方:代码中加入方法,该方法及其参数的序列化成SOAP消息,消息契约编码让其内在的信息编码序列的信息成为一个字节数组,消息契约编码压缩的字节数组第二个字节数组,字节数组发送传输层。
    接收方:传输层接收字节数组,消息契约编码的字节数组解压到第二字节数组,消息契约编码让其内在的信息编码化的第二个字节数组消息,该方法及其参并行化到SOAP消息,方法是被监听的。

    这个消息契约编码分为几个类:
    CompactMessageEncoder //这个类提供了信息编码实施。
    CompactMessageEncoderFactory //这个类是负责提供契约信息编码实例。
    CompactMessageEncodingBindingElement //这个类负责通道的协议约束规范。
    CompactMessageEncodingElement //这个类使信息编码通过增加应用程序配置文件。

附件: image1.jpg

               
图2 消息通道编码器静态类图

    压缩方法:契约消息编码器是使用gzip压缩的NET Framework范围内执行的,是调用System.IO.Compression.GZipStream名字空间类中。
    加入引用CompactMessageEncoder.dll,修改app.config文件引用,应用程序必须要在客户端和服务器端。

压缩缓冲代码:

  1. private static ArraySegment<byte> CompressBuffer(ArraySegment<byte> buffer, BufferManager bufferManager, int messageOffset)
  2.         {
  3.             // Create a memory stream for the final message
  4.             MemoryStream memoryStream = new MemoryStream();
  5.             // Copy the bytes that should not be compressed into the stream
  6.             memoryStream.Write(buffer.Array, 0, messageOffset);
  7.             // Compress the message into the stream
  8.             using (GZipStream gzStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
  9.             {
  10.                 gzStream.Write(buffer.Array, messageOffset, buffer.Count);
  11.             }
  12.             // Convert the stream into a bytes array
  13.             byte[] compressedBytes = memoryStream.ToArray();
  14.             // Allocate a new buffer to hold the new bytes array
  15.             byte[] bufferedBytes = bufferManager.TakeBuffer(compressedBytes.Length);
  16.             // Copy the compressed data into the allocated buffer
  17.             Array.Copy(compressedBytes, 0, bufferedBytes, 0, compressedBytes.Length);
  18.             // Release the original buffer we got as an argument
  19.             bufferManager.ReturnBuffer(buffer.Array);
  20.             // Create a new ArraySegment that points to the new message buffer
  21.             ArraySegment<byte> byteArray = new ArraySegment<byte>(bufferedBytes, messageOffset, compressedBytes.Length - messageOffset);
  22.             return byteArray;
  23.         }
复制代码

解压缓冲代码:

  1.   private static ArraySegment<byte> DecompressBuffer(ArraySegment<byte> buffer, BufferManager bufferManager)
  2.         {
  3.             // Create a new memory stream, and copy into it the buffer to decompress
  4.             MemoryStream memoryStream = new MemoryStream(buffer.Array, buffer.Offset, buffer.Count);
  5.             // Create a memory stream to store the decompressed data
  6.             MemoryStream decompressedStream = new MemoryStream();
  7.             // The totalRead stores the number of decompressed bytes
  8.             int totalRead = 0;
  9.             int blockSize = 1024;
  10.             // Allocate a temporary buffer to use with the decompression
  11.             byte[] tempBuffer = bufferManager.TakeBuffer(blockSize);
  12.             // Uncompress the compressed data
  13.             using (GZipStream gzStream = new GZipStream(memoryStream, CompressionMode.Decompress))
  14.             {
  15.                 while (true)
  16.                 {
  17.                     // Read from the compressed data stream
  18.                     int bytesRead = gzStream.Read(tempBuffer, 0, blockSize);
  19.                     if (bytesRead == 0)
  20.                         break;
  21.                     // Write to the decompressed data stream
  22.                     decompressedStream.Write(tempBuffer, 0, bytesRead);
  23.                     totalRead += bytesRead;
  24.                 }
  25.             }
  26.             // Release the temporary buffer
  27.             bufferManager.ReturnBuffer(tempBuffer);
  28.             // Convert the decompressed data stream into bytes array
  29.             byte[] decompressedBytes = decompressedStream.ToArray();
  30.             // Allocate a new buffer to store the message
  31.             byte[] bufferManagerBuffer = bufferManager.TakeBuffer(decompressedBytes.Length + buffer.Offset);
  32.             // Copy the bytes that comes before the compressed message in the buffer argument
  33.             Array.Copy(buffer.Array, 0, bufferManagerBuffer, 0, buffer.Offset);
  34.             // Copy the decompressed data
  35.             Array.Copy(decompressedBytes, 0, bufferManagerBuffer, buffer.Offset, decompressedBytes.Length);
  36.             // Create a new ArraySegment that points to the new message buffer
  37.             ArraySegment<byte> byteArray = new ArraySegment<byte>(bufferManagerBuffer, buffer.Offset, decompressedBytes.Length);
  38.             // Release the original message buffer
  39.             bufferManager.ReturnBuffer(buffer.Array);
  40.             return byteArray;
  41.         }
复制代码

改变服务端配置

  加入消息契约编码器之前app.config的实例:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.     <system.serviceModel>
  4.         <services>
  5.             <service name="Server.MyService">
  6.                 <endpoint
  7.                     address="net.tcp://localhost:1234/MyService"
  8.                     binding="netTcpBinding"
  9.                     contract="Server.IMyService" />
  10.             </service>
  11.         </services>
  12.     </system.serviceModel>
  13. </configuration>
复制代码

加入消息契约编码器后app.config的例子:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.     <system.serviceModel>
  4.         <services>
  5.             <service name="Server.MyService">
  6.             <!-- Set the binding of the endpoint to customBinding -->               
  7.             <endpoint
  8.                     address="net.tcp://localhost:1234/MyService"
  9.                     binding="customBinding"
  10.                     contract="Server.IMyService" />
  11.             </service>
  12.         </services>
  13.         <!-- Defines a new customBinding that contains the compactMessageEncoding -->       
  14.         <bindings>
  15.             <customBinding>
  16.                 <binding name="compactBinding">
  17.                     <compactMessageEncoding>
  18.                 <!-- Defines the inner message encoder as binary encoder -->                       
  19.                 <binaryMessageEncoding />
  20.                     </compactMessageEncoding>
  21.                     <tcpTransport />
  22.                 </binding>
  23.             </customBinding>
  24.         </bindings>
  25.     <!-- Adds the extension dll so the WCF can find the compactMessageEncoding -->
  26.         <extensions>
  27.             <bindingElementExtensions>
  28.                 <add name="compactMessageEncoding" type="Amib.WCF.CompactMessageEncodingElement, CompactMessageEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  29.             </bindingElementExtensions>
  30.         </extensions>
  31.     </system.serviceModel>
  32. </configuration>
复制代码

客户端配置变化

加入消息契约编码器之前app.config的实例:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.     <system.serviceModel>
  4.         <client>
  5.             <endpoint
  6.                 address="net.tcp://localhost:1234/MyService"
  7.                 binding="customBinding"
  8.                 bindingConfiguration="compactBinding"
  9.                 contract="Client.IMyService" />
  10.         </client>
  11.     </system.serviceModel>
  12. </configuration>
复制代码

加入消息契约编码器后app.config的例子:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.     <system.serviceModel>
  4.         <client>
  5.             <endpoint
  6.                 address="net.tcp://localhost:1234/MyService"
  7.                 binding="customBinding"
  8.                 bindingConfiguration="compactBinding"
  9.                 contract="Client.IMyService" />
  10.         </client>
  11.         <!-- Defines a new customBinding that contains the compactMessageEncoding -->       
  12.         <bindings>
  13.             <customBinding>
  14.                 <binding name="compactBinding">
  15.                     <compactMessageEncoding>
  16.                         <binaryMessageEncoding/>
  17.                     </compactMessageEncoding>
  18.                     <tcpTransport />
  19.                 </binding>
  20.             </customBinding>
  21.         </bindings>
  22.        
  23.     <!-- Adds the extension dll so the WCF can find the compactMessageEncoding -->
  24.         <extensions>
  25.             <bindingElementExtensions>
  26.                 <add name="compactMessageEncoding" type="Amib.WCF.CompactMessageEncodingElement, CompactMessageEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  27.             </bindingElementExtensions>
  28.         </extensions>
  29.     </system.serviceModel>
  30. </configuration>
复制代码

这种压缩方法,消息堵塞的几率很小。使用CompactMessageEncoder在同一台机器运行客户端和服务器上可能会降低效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值