65.9K
CodeProject 正在变化。 阅读更多。
Home

CompactMessageEncoder

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (11投票s)

2007年9月5日

Ms-PL

5分钟阅读

viewsIcon

124104

downloadIcon

1171

CompactMessageEncoder 可在通信通道上透明地压缩消息,从而降低网络使用量。

引言

有很多关于 WCF 如何工作的文章,所以在这篇文章中我将跳过这部分。本文代码的目的是让用户能够压缩使用 WCF 在机器之间传输的数据。(为简单起见,我使用 WCF 这个术语,而不是指具体的组件。)

当 WCF 用于两个实体之间的通信时,它会创建一个通信通道来传输消息。消息包含请求和响应的数据。WCF 使用特殊的编码器将请求和响应数据转换为字节数组。

CompactMessageEncoder 会连接到客户端和服务器两端的通道。当消息传输时,编码器会在发送端进行压缩,在接收端进行解压缩,因此是透明的。

背景

我实现此类编码器的驱动力是需要将一个大文件(XML 格式)从一台机器传输到另一台机器,而连接速度有限。

我选择通过 WCF 来实现这一点(传输完成后还有更多工作要做)。我不想编写一个在发送端压缩、在接收端解压缩的特殊函数,而是希望配置通道本身来完成这项工作。这样,压缩就可以在任何契约上重用。

在互联网上搜索和研究后,我发现编写消息编码器是最简单的方法。真正的问题是如何编写消息编码器,因为 MSDN 上没有任何示例(至少我没找到)。我找到的任何示例都已过时,因为它们是在 WCF 正式发布之前编写的。然而,这些示例给了我如何编写消息编码器的坚实基础。

我的实现基于我在机器上安装的一个示例,但我不知道它的来源。我在 C:\Program Files\Microsoft SDKs\WinFX\samples\Allsamples\Indigo\TechnologySamples\Extensibility\
Channels\MessageEncoder\Compression\CS
中找到了它。该示例正是我最初想要的,但可惜它无法工作。它为 WCF 的早期版本编写,包含已不再存在的接口和类。从这个示例中,我获得了如何压缩和解压缩消息的知识。我还注意到实现中有两个小错误,我已经修复了它们。

我从 Nicholas Allen 的博客 http://blogs.msdn.com/drnick/archive/2006/05/09/592933.aspx 中获得了一些帮助。但是,它不包含如何将消息编码器添加到配置文件中的代码。

我还使用了 Reflector 来查看其他消息编码器是如何工作的,例如

System.ServiceModel.Channels.BinaryMessageEncoderFactory.BinaryMessageEncoder.

工作原理

Compact message encoder 的想法是在通道上挂钩消息的发送和接收,在发送端压缩它们,然后在接收端解压缩它们。要植入消息编码器,需要在 customBinding 中添加一个绑定元素。

由于压缩本身不需要进行编码,它会使用另一个消息编码器来完成,例如 BinaryMessageEncoder

正常服务方法执行

发送端

  1. 在代码中调用一个方法。
  2. 方法及其参数被序列化为 SOAP 消息。
  3. 消息编码器将消息序列化为字节数组。
  4. 字节数组通过传输层发送。

接收端

  1. 传输层接收字节数组。
  2. 消息编码器将字节数组反序列化为消息。
  3. 方法及其参数被反序列化为 SOAP 消息。
  4. 调用实际的方法。

当添加了 compact message encoder 时,方法调用会稍有改变

发送端

  1. 在代码中调用一个方法。
  2. 方法及其参数被序列化为 SOAP 消息。
  3. Compact message encoder 让其内部消息编码器将消息序列化为字节数组。
  4. Compact message encoder 将字节数组压缩为第二个字节数组。
  5. 字节数组通过传输层发送。

接收端

  1. 传输层接收字节数组。
  2. Compact message encoder 将字节数组解压缩为第二个字节数组。
  3. Compact message encoder 让其内部消息编码器将第二个字节数组反序列化为消息。
  4. 方法及其参数被反序列化为 SOAP 消息。
  5. 调用实际的方法。

Compact message encoder 分为几个类

CompactMessageEncoder - 此类提供消息编码器实现。

CompactMessageEncoderFactory - 此类负责提供 Compact message encoder 实例。

CompactMessageEncodingBindingElement - 这是参与通道绑定堆栈的类。

CompactMessageEncodingElement - 这是允许通过应用程序配置文件添加消息编码器的类。

压缩

CompactMessageEncoder 使用 .NET Framework 中实现的 GZip 压缩。这是通过 System.IO.Compression.GZipStream 实现的。

如何使用

添加对 CompactMessageEncoder.dll 的引用

在更改 app.config 之前,您必须添加对 CompactMessageEncoder.dll 的引用。这必须在客户端和服务器应用程序上都完成。

服务器配置更改

这是添加 compact message encoder 之前的 app.config 示例

<?xml version="1.0" encoding="utf-8" ?>
<configuration> 
    <system.serviceModel> 
        <services> 
            <service name="Server.MyService">
                <endpoint 
                    address="net.tcp://:1234/MyService" 
                    binding="netTcpBinding"
                    contract="Server.IMyService" /> 
            </service> 
        </services> 
    </system.serviceModel>
</configuration>

这是添加 compact message encoder 之后的 app.config 示例

<?xml version="1.0" encoding="utf-8" ?>
<configuration> 
    <system.serviceModel> 
        <services> 
            <service name="Server.MyService">

            <!-- Set the binding of the endpoint to customBinding -->
            <endpoint 
                    address="net.tcp://:1234/MyService" 
                    binding="customBinding"
                    contract="Server.IMyService" /> 
            </service> 
        </services> 

        <!-- Defines a new customBinding that contains the compactMessageEncoding -->
        <bindings> 
            <customBinding> 
                <binding name="compactBinding"> 
                    <compactMessageEncoding>

                <!-- Defines the inner message encoder as binary encoder -->
                <binaryMessageEncoding /> 
                    </compactMessageEncoding> 
                    <tcpTransport /> 
                </binding> 
            </customBinding> 
        </bindings> 

    <!-- Adds the extension DLL so the WCF can find the compactMessageEncoding -->
        <extensions> 
            <bindingElementExtensions> 
                <add name="compactMessageEncoding" 
		type="Amib.WCF.CompactMessageEncodingElement, 
		CompactMessageEncoder, Version=1.0.0.0, 
		Culture=neutral, PublicKeyToken=null" />
            </bindingElementExtensions> 
        </extensions> 

     </system.serviceModel>
</configuration>

客户端配置更改

这是添加 compact message encoder 之前的 app.config 示例

<?xml version="1.0" encoding="utf-8" ?>
<configuration> 
    <system.serviceModel> 
        <client> 
            <endpoint
                address="net.tcp://:1234/MyService"
                binding="customBinding"
                bindingConfiguration="compactBinding"
                contract="Client.IMyService" />
        </client> 
    </system.serviceModel>
</configuration>

This is an example of the app.config after adding the compact message encoder:
<?xml version="1.0" encoding="utf-8" ?>
<configuration> 
    <system.serviceModel> 
        <client> 
            <endpoint
                address="net.tcp://:1234/MyService"
                binding="customBinding"
                bindingConfiguration="compactBinding"
                contract="Client.IMyService" />
        </client> 

        <!-- Defines a new customBinding that contains the compactMessageEncoding --> 
        <bindings> 
            <customBinding> 
                <binding name="compactBinding">
                    <compactMessageEncoding> 
                        <binaryMessageEncoding/> 
                    </compactMessageEncoding>
                    <tcpTransport /> 
                </binding> 
            </customBinding> 
        </bindings>
        
    <!-- Adds the extension DLL so the WCF can find the compactMessageEncoding -->
        <extensions> 
            <bindingElementExtensions> 
                <add name="compactMessageEncoding" 
		type="Amib.WCF.CompactMessageEncodingElement, 
		CompactMessageEncoder, Version=1.0.0.0, 
		Culture=neutral, PublicKeyToken=null" /> 
            </bindingElementExtensions> 
        </extensions>

    </system.serviceModel>
</configuration>

限制 & 解决方法

  1. 所有消息都会在通道上进行压缩,即使这会导致消息膨胀。当消息很小时会发生这种情况。
  2. CompactMessageEncoder 仅支持 Buffered 传输,不支持 Streamed 传输。
  3. 在同一台机器上运行的客户端和服务器上使用 CompactMessageEncoder 可能会降低性能。
  4. WCF 配置编辑器对 CompactMessageEncoder 的支持不完全,因此部分配置必须使用 XML 文本编辑器手动完成。
  5. 每次 WCF 配置编辑器打开 app.config 文件时,它都会询问 CompactMessageEncoder.dll 的安全性。我不知道如何摆脱这种行为。
  6. binaryMessageEncoding textMessageEncoding 的配置无法在 WCF 配置编辑器中进行编辑。要解决此问题,请从 app.config 中删除 compactMessageEncoding 元素(保留其子元素),然后使用 WCF 配置编辑器打开它。之后,再将 compactMessageEncoding 元素添加回来。

免责声明

本代码和信息“按原样”提供,不附带任何形式的保修,无论是明示的还是暗示的,包括但不限于对适销性和/或特定用途适用性的暗示保证。

历史

  • 2007年9月5日:初始版本
© . All rights reserved.